From ccf67f837eaa09e979b672dbb9da96ef498ffb23 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Wed, 1 Apr 2026 18:55:28 +0200 Subject: [PATCH 1/2] fix(ai-client): prevent infinite tool call loop when finishReason is stop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the server-side agent loop executes a tool and the model finishes with finishReason 'stop', the client should not auto-send another request. The model is done — no continuation needed. Previously, the client checked only if the last message part was a tool-result and all tools were complete, triggering auto-send regardless of finishReason. This caused infinite loops with non-OpenAI providers that respond minimally after tool execution. --- packages/typescript/ai-client/src/chat-client.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/typescript/ai-client/src/chat-client.ts b/packages/typescript/ai-client/src/chat-client.ts index 4b0e40f70..7272394a3 100644 --- a/packages/typescript/ai-client/src/chat-client.ts +++ b/packages/typescript/ai-client/src/chat-client.ts @@ -657,11 +657,18 @@ export class ChatClient { await this.drainPostStreamActions() // Continue conversation if the stream ended with a tool result (server tool completed) + // but ONLY if the model indicated it wants to continue (finishReason !== 'stop'). + // When finishReason is 'stop', the model is done — don't re-send. if (streamCompletedSuccessfully) { const messages = this.processor.getMessages() const lastPart = messages.at(-1)?.parts.at(-1) + const { finishReason } = this.processor.getState() - if (lastPart?.type === 'tool-result' && this.shouldAutoSend()) { + if ( + lastPart?.type === 'tool-result' && + finishReason !== 'stop' && + this.shouldAutoSend() + ) { try { await this.checkForContinuation() } catch (error) { From 1b5c400c182736a1dedc356087a673175b9a5536 Mon Sep 17 00:00:00 2001 From: Alem Tuzlak Date: Wed, 1 Apr 2026 18:57:28 +0200 Subject: [PATCH 2/2] chore: add changeset for infinite tool loop fix --- .changeset/fix-infinite-tool-loop.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/fix-infinite-tool-loop.md diff --git a/.changeset/fix-infinite-tool-loop.md b/.changeset/fix-infinite-tool-loop.md new file mode 100644 index 000000000..9d3f4de1c --- /dev/null +++ b/.changeset/fix-infinite-tool-loop.md @@ -0,0 +1,7 @@ +--- +'@tanstack/ai-client': patch +--- + +fix: prevent infinite tool call loop when server tool finishes with stop + +When the server-side agent loop executes a tool and the model finishes with `finishReason: 'stop'`, the client no longer auto-sends another request. Previously this caused infinite loops with non-OpenAI providers that respond minimally after tool execution.