Cancelled/denied tool calls are indistinguishable from genuine failures
@tombeckenham is already working on this.
Since Sep 17, 2026.
- Dominant language
- TypeScript
- Stars
- 3.1k
- Forks
- 331
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 160
Description
TanStack AI version
@tanstack/ai v0.54.0 (also v0.53.0)
Framework/Library version
Framework-agnostic (plain Node.js)
Describe the bug and the steps to reproduce it
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):
- Cancel (
:807-813):{ error: 'Tool execution cancelled' } - Deny (
:912-923,:1001-1009, viadeniedApprovalResult:486-490):{ error: 'User declined tool execution' }
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)
https://stackblitz.com/github/oztune/tanstack-ai-cancel-repro
Screenshots or Videos (Optional)
No response
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.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.