Skip to content

fix(ai-client): prevent infinite tool call loop when finishReason is stop#412

Merged
AlemTuzlak merged 2 commits intomainfrom
fix/infinite-tool-loop
Apr 1, 2026
Merged

fix(ai-client): prevent infinite tool call loop when finishReason is stop#412
AlemTuzlak merged 2 commits intomainfrom
fix/infinite-tool-loop

Conversation

@AlemTuzlak
Copy link
Copy Markdown
Contributor

@AlemTuzlak AlemTuzlak commented Apr 1, 2026

Summary

  • 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, the continuation logic only checked if the last message part was tool-result and all tools were complete — it didn't check finishReason
  • This caused infinite loops with non-OpenAI providers (Anthropic, OpenRouter, Gemini, etc.) that respond minimally after tool execution

Root cause

The ChatClient.streamResponse() continuation logic at the end of a successful stream:

if (lastPart?.type === 'tool-result' && this.shouldAutoSend()) {
  await this.checkForContinuation()
}

When a server tool executes and the model responds with finishReason: 'stop', the last part in the message is tool-result (from TOOL_CALL_END.result). areAllToolsComplete() returns true. The client auto-sends, the model calls the tool again, and the cycle repeats.

Fix

Check finishReason before auto-sending:

if (lastPart?.type === 'tool-result' && finishReason !== 'stop' && this.shouldAutoSend()) {

When the model says stop, it's done — no continuation needed.

Test plan

  • pnpm --filter @tanstack/ai-client run test:lib — 198 tests pass
  • Manual test: "recommend a guitar to me" with Anthropic and OpenRouter no longer loops
  • OpenAI tool calls still work correctly (model calls recommendGuitar client tool)

Generated with Claude Code

Summary by CodeRabbit

Bug Fixes

  • Fixed an infinite loop issue when using tools with non-OpenAI AI providers. The client now properly respects when a model signals completion and avoids unnecessary retries.

…stop

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.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 1, 2026

🚀 Changeset Version Preview

1 package(s) bumped directly, 10 bumped as dependents.

🟩 Patch bumps

Package Version Reason
@tanstack/ai-client 0.7.5 → 0.7.6 Changeset
@tanstack/ai-preact 0.6.10 → 0.6.11 Dependent
@tanstack/ai-react 0.7.6 → 0.7.7 Dependent
@tanstack/ai-solid 0.6.10 → 0.6.11 Dependent
@tanstack/ai-svelte 0.6.10 → 0.6.11 Dependent
@tanstack/ai-vue 0.6.10 → 0.6.11 Dependent
@tanstack/ai-vue-ui 0.1.21 → 0.1.22 Dependent
@tanstack/smoke-tests-e2e 0.0.31 → 0.0.32 Dependent
ts-svelte-chat 0.1.25 → 0.1.26 Dependent
ts-vue-chat 0.1.25 → 0.1.26 Dependent
vanilla-chat 0.0.25 → 0.0.26 Dependent

@nx-cloud
Copy link
Copy Markdown

nx-cloud bot commented Apr 1, 2026

View your CI Pipeline Execution ↗ for commit 1b5c400

Command Status Duration Result
nx affected --targets=test:sherif,test:knip,tes... ✅ Succeeded 1m 47s View ↗
nx run-many --targets=build --exclude=examples/** ✅ Succeeded 2s View ↗

☁️ Nx Cloud last updated this comment at 2026-04-01 17:04:24 UTC

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 1, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 653915a5-e6d1-49d0-a84a-c74404e188d1

📥 Commits

Reviewing files that changed from the base of the PR and between cc77237 and 1b5c400.

📒 Files selected for processing (2)
  • .changeset/fix-infinite-tool-loop.md
  • packages/typescript/ai-client/src/chat-client.ts

📝 Walkthrough

Walkthrough

A patch release for @tanstack/ai-client introduces a fix to prevent infinite tool call loops by preventing automatic resend of conversation flow when the model returns finishReason: 'stop' after tool execution.

Changes

Cohort / File(s) Summary
Documentation
.changeset/fix-infinite-tool-loop.md
New changeset entry documenting the patch release and behavior change for infinite tool loop prevention.
Core Client Logic
packages/typescript/ai-client/src/chat-client.ts
Modified streamResponse() post-stream teardown logic to gate auto-resend continuation on finishReason !== 'stop', preventing re-sending when stream completes with stop reason.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Poem

🐰 A loop that spun round and round,
Now gently halts when 'stop' is found,
The finish line brings peace at last,
No more infinite loops so vast! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main fix: preventing infinite tool call loops when finishReason is 'stop'.
Description check ✅ Passed The description includes detailed explanation of the issue, root cause, fix, and test plan. However, it lacks the checklist items from the required template.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/infinite-tool-loop

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 1, 2026

Open in StackBlitz

@tanstack/ai

npm i https://pkg.pr.new/@tanstack/ai@412

@tanstack/ai-anthropic

npm i https://pkg.pr.new/@tanstack/ai-anthropic@412

@tanstack/ai-client

npm i https://pkg.pr.new/@tanstack/ai-client@412

@tanstack/ai-devtools-core

npm i https://pkg.pr.new/@tanstack/ai-devtools-core@412

@tanstack/ai-elevenlabs

npm i https://pkg.pr.new/@tanstack/ai-elevenlabs@412

@tanstack/ai-event-client

npm i https://pkg.pr.new/@tanstack/ai-event-client@412

@tanstack/ai-fal

npm i https://pkg.pr.new/@tanstack/ai-fal@412

@tanstack/ai-gemini

npm i https://pkg.pr.new/@tanstack/ai-gemini@412

@tanstack/ai-grok

npm i https://pkg.pr.new/@tanstack/ai-grok@412

@tanstack/ai-groq

npm i https://pkg.pr.new/@tanstack/ai-groq@412

@tanstack/ai-ollama

npm i https://pkg.pr.new/@tanstack/ai-ollama@412

@tanstack/ai-openai

npm i https://pkg.pr.new/@tanstack/ai-openai@412

@tanstack/ai-openrouter

npm i https://pkg.pr.new/@tanstack/ai-openrouter@412

@tanstack/ai-preact

npm i https://pkg.pr.new/@tanstack/ai-preact@412

@tanstack/ai-react

npm i https://pkg.pr.new/@tanstack/ai-react@412

@tanstack/ai-react-ui

npm i https://pkg.pr.new/@tanstack/ai-react-ui@412

@tanstack/ai-solid

npm i https://pkg.pr.new/@tanstack/ai-solid@412

@tanstack/ai-solid-ui

npm i https://pkg.pr.new/@tanstack/ai-solid-ui@412

@tanstack/ai-svelte

npm i https://pkg.pr.new/@tanstack/ai-svelte@412

@tanstack/ai-vue

npm i https://pkg.pr.new/@tanstack/ai-vue@412

@tanstack/ai-vue-ui

npm i https://pkg.pr.new/@tanstack/ai-vue-ui@412

@tanstack/preact-ai-devtools

npm i https://pkg.pr.new/@tanstack/preact-ai-devtools@412

@tanstack/react-ai-devtools

npm i https://pkg.pr.new/@tanstack/react-ai-devtools@412

@tanstack/solid-ai-devtools

npm i https://pkg.pr.new/@tanstack/solid-ai-devtools@412

commit: 1b5c400

@AlemTuzlak AlemTuzlak merged commit c0ae603 into main Apr 1, 2026
7 checks passed
@AlemTuzlak AlemTuzlak deleted the fix/infinite-tool-loop branch April 1, 2026 17:25
@github-actions github-actions bot mentioned this pull request Apr 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants