TanStack AI version
0.48.0
Framework/Library version
@tanstack/ai-client: 0.26.0 | @tanstack/ai-svelte: 0.18.3
Describe the bug and the steps to reproduce it
ChatClient.stop() aborts the current request, but it does not invalidate client-tool executions or native interrupt resolutions that are still pending for that turn.
When an automatically executed client tool settles after stop(), its result can start another connection and continue the conversation.
onToolCall starts and tracks the client-tool promise. A fulfilled tool is passed to addToolResultForClientTool() as output-available, while a rejected tool is caught and passed to the same method as output-error.
stop() aborts the active connection, discards queued sends and resets the local generating state. It does not invalidate the pending tool promise or the interrupt manager state associated with that turn.
When the tool later settles, addToolResultForClientTool() resolves the matching native client-tool interrupt. The interrupt manager then starts an asynchronous batch submission, and submitInterruptBatch() starts a child resume run.
The resulting sequence is:
RUN_FINISHED (outcome: interrupt)
client tool remains pending
client.stop()
client tool settles
connect(... resume ...) starts after Stop
Expected behavior
Calling ChatClient.stop() while a client tool is pending should prevent asynchronous continuation work from that turn from starting another connection. Late client-tool success or error should be ignored before it mutates the transcript, and deferred or in-flight native interrupt submissions should settle without restoring an error or resuming the stopped turn.
ChatClient should track a continuation generation across client-tool execution and native resume submission. stop() should invalidate that generation, clear lastResume and the active interrupt submission, and reset the interrupt manager. The interrupt manager should also invalidate its active submission on reset so a late submission result cannot publish stale state.
After Stop, the client should preserve the transcript accumulated before Stop, leave resuming false and allow a later explicit user message to start without resume or parentRunId. The underlying client-tool promise does not need to be cancelled because arbitrary tools may not support cancellation.
This issue covers the client lifecycle only. ChatClient.stop() does not cancel a durable server run or clear server-authoritative persisted interrupts; that requires a separate server cancellation path.
Related issues
Your Minimal, Reproducible Example - (Sandbox Highly Recommended)
https://stackblitz.com/edit/vitejs-vite-l3sbbrcs?file=src%2Fmain.ts
Do you intend to try to help solve this bug with your own PR?
Yes, I am also opening a PR that solves the problem along side this issue
Terms & Code of Conduct
TanStack AI version
0.48.0
Framework/Library version
@tanstack/ai-client: 0.26.0 | @tanstack/ai-svelte: 0.18.3
Describe the bug and the steps to reproduce it
ChatClient.stop()aborts the current request, but it does not invalidate client-tool executions or native interrupt resolutions that are still pending for that turn.When an automatically executed client tool settles after
stop(), its result can start another connection and continue the conversation.onToolCallstarts and tracks the client-tool promise. A fulfilled tool is passed toaddToolResultForClientTool()asoutput-available, while a rejected tool is caught and passed to the same method asoutput-error.stop()aborts the active connection, discards queued sends and resets the local generating state. It does not invalidate the pending tool promise or the interrupt manager state associated with that turn.When the tool later settles,
addToolResultForClientTool()resolves the matching native client-tool interrupt. The interrupt manager then starts an asynchronous batch submission, andsubmitInterruptBatch()starts a child resume run.The resulting sequence is:
Expected behavior
Calling
ChatClient.stop()while a client tool is pending should prevent asynchronous continuation work from that turn from starting another connection. Late client-tool success or error should be ignored before it mutates the transcript, and deferred or in-flight native interrupt submissions should settle without restoring an error or resuming the stopped turn.ChatClientshould track a continuation generation across client-tool execution and native resume submission.stop()should invalidate that generation, clearlastResumeand the active interrupt submission, and reset the interrupt manager. The interrupt manager should also invalidate its active submission on reset so a late submission result cannot publish stale state.After Stop, the client should preserve the transcript accumulated before Stop, leave
resumingfalse and allow a later explicit user message to start withoutresumeorparentRunId. The underlying client-tool promise does not need to be cancelled because arbitrary tools may not support cancellation.This issue covers the client lifecycle only.
ChatClient.stop()does not cancel a durable server run or clear server-authoritative persisted interrupts; that requires a separate server cancellation path.Related issues
ChatClient.append()can resolve on a provider-iterationRUN_FINISHEDbefore the final AG-UI run outcome #1192 concerns multiple provider lifecycle terminals within onechat()call. This issue occurs after a valid outer interrupt terminal and reproduces even when the stream completes normally.ChatClientsends a legacy continuation after a server-handled client-tool input error #1189 concerns a server-owned tool result that incorrectly enters the legacy continuation path. This issue uses the native interrupt path, which returns before legacy continuation is considered.Your Minimal, Reproducible Example - (Sandbox Highly Recommended)
https://stackblitz.com/edit/vitejs-vite-l3sbbrcs?file=src%2Fmain.ts
Do you intend to try to help solve this bug with your own PR?
Yes, I am also opening a PR that solves the problem along side this issue
Terms & Code of Conduct