You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Cancelled/denied tool calls are indistinguishable from genuine failures #1386
A cancelled or user-denied tool call is emitted with the same shape as a genuine failure, so a consumer can't tell them apart. Both produce state: 'output-error' + { error: <hardcoded string> } (packages/ai/src/activities/chat/tools/tool-calls.ts):
That is identical to real failures — unknown tool (:787), arg-parse (:826), validation (:848). The only discriminator is the hardcoded English string; there's no distinct state, since ToolResultState is 'streaming' | 'complete' | 'error' (types.ts:76).
The framework does know the outcome structurally — the InterruptStore record's status is 'cancelled' — but that's discarded when the TOOL_CALL_RESULT is built, and only { error: <string> } survives into the persisted ModelMessage a UI re-hydrates from. So after reload, matching that localizable string is the only option left.
Runnable repro (no API key) + verified output are in the linked sandbox: an approval-deny and a genuine execute() throw come back byte-identical except for the error string.
Expected: a structured, persistable signal for "user cancelled/declined", distinct from a failure — a terminal state, a discriminator on the result, or (minimally) exported constants for the two strings. Ideally carry through the outcome the InterruptStore already records.
Your Minimal, Reproducible Example - (Sandbox Highly Recommended)
Do you intend to try to help solve this bug with your own PR?
No response
Terms & Code of Conduct
I agree to follow this project's Code of Conduct
I understand that if my bug cannot be reliable reproduced in a debuggable environment, it will probably not be fixed and this issue may even be closed.
Activity