Skip to content

fix(ai-client): settle status to ready on a message-less terminal run (#421)#749

Open
valeriudev wants to merge 1 commit into
TanStack:mainfrom
valeriudev:fix/issue-421-client-tool-status
Open

fix(ai-client): settle status to ready on a message-less terminal run (#421)#749
valeriudev wants to merge 1 commit into
TanStack:mainfrom
valeriudev:fix/issue-421-client-tool-status

Conversation

@valeriudev

@valeriudev valeriudev commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Fixes #421

🎯 Changes

useChat stayed in a non-ready status after a client tool call when the continuation run closed with a bare RUN_FINISHED { finishReason: 'stop' } and no assistant message.

Status only reaches ready through the processor's onStreamEnd callback, which StreamProcessor.finalizeStream() fires only when an assistant message exists — so a message-less terminal run left status stuck at submitted. The fix normalizes status to ready in streamResponse's finally block on the terminal, non-continuing path, guarded on status !== 'ready' so the normal onStreamEnd path is a no-op. Kept in ai-client rather than the core processor: onStreamEnd is message-scoped by design (enforced by existing processor tests) and status is a client-owned concern.

Regression coverage in chat-client-client-tool-status.test.ts: the #421 repro, an exactly-once ready guard on the normal text path, and the first-run bare-RUN_FINISHED case.

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally (@tanstack/ai-client: test:lib, test:types, test:eslint).

🚀 Release Impact

  • This change affects published code, and I have generated a changeset (@tanstack/ai-client patch).

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a fallback in ChatClient.streamResponse to set status to "ready" when auto-continuation doesn't run for message-less terminal RUN_FINISHED events, documents the fix in a changeset, and adds Vitest regression tests for the primary and related edge cases.

Changes

Status settlement for message-less terminal runs

Layer / File(s) Summary
Status normalization in streamResponse
packages/ai-client/src/chat-client.ts, .changeset/chat-client-tool-status-ready.md
Adds an explicit setStatus('ready') fallback when auto-continuation doesn't occur and status is not already ready, and updates the inline comment about tool-result error forwarding. Changeset documents the behavioral fix for continuation runs ending with RUN_FINISHED { finishReason: 'stop' } and no assistant message.
Regression tests for status settlement
packages/ai-client/tests/chat-client-client-tool-status.test.ts
Adds Vitest tests covering issue #421: two-round stream with a client tool call gated by a deferred promise and a message-less terminal continuation, plus sibling tests for plain-text runs and immediate message-less terminal first runs; all assert final client status is ready and onStatusChange emits the expected final value.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Suggested reviewers

  • tombeckenham

Poem

A status stuck in streaming mode,
Now finds the ready-state road.
One gentle check, no messages missed—
The rabbit hops; the client’s fixed. 🐰✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main fix: settling status to ready on a message-less terminal run, and references the issue number.
Linked Issues check ✅ Passed The PR directly addresses issue #421 by fixing the status-settling bug that occurred when a client tool continuation closed with only RUN_FINISHED and no assistant message.
Out of Scope Changes check ✅ Passed All changes are scoped to fixing issue #421: a changeset file, client status normalization logic, clarified comment, and targeted regression tests with no unrelated modifications.
Description check ✅ Passed The PR description comprehensively covers changes, motivation, and includes all required checklist items and changeset generation.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/ai-client/tests/chat-client-client-tool-status.test.ts (1)

1-175: ⚡ Quick win

Move this unit test alongside the source file to match repo test-placement rules.

This test should live next to packages/ai-client/src/chat-client.ts as a *.test.ts sibling instead of under packages/ai-client/tests/.

As per coding guidelines, **/*.test.ts: “Place unit tests alongside source code in *.test.ts files”.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ai-client/tests/chat-client-client-tool-status.test.ts` around lines
1 - 175, The test file
packages/ai-client/tests/chat-client-client-tool-status.test.ts must be moved
next to the implementation to follow repo rules: relocate it to
packages/ai-client/src/chat-client.test.ts (sibling of chat-client.ts), update
any relative imports accordingly (ensure imports of ChatClient,
createMockConnectionAdapter, createTextChunks, createToolCallChunks and
ConnectConnectionAdapter still resolve), and run the tests to confirm paths are
correct; no code changes to tests themselves are required beyond adjusting
import paths.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/ai-client/tests/chat-client-client-tool-status.test.ts`:
- Around line 1-175: The test file
packages/ai-client/tests/chat-client-client-tool-status.test.ts must be moved
next to the implementation to follow repo rules: relocate it to
packages/ai-client/src/chat-client.test.ts (sibling of chat-client.ts), update
any relative imports accordingly (ensure imports of ChatClient,
createMockConnectionAdapter, createTextChunks, createToolCallChunks and
ConnectConnectionAdapter still resolve), and run the tests to confirm paths are
correct; no code changes to tests themselves are required beyond adjusting
import paths.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f381bc11-2412-407a-b197-4b345d135993

📥 Commits

Reviewing files that changed from the base of the PR and between 984ac3c and e274294.

📒 Files selected for processing (3)
  • .changeset/chat-client-tool-status-ready.md
  • packages/ai-client/src/chat-client.ts
  • packages/ai-client/tests/chat-client-client-tool-status.test.ts

When a continuation run after a client tool call closes with a bare
RUN_FINISHED{stop} and no assistant message, the processor's onStreamEnd
never fires, so status stayed stuck at submitted. Normalize status to
ready on the terminal, non-continuing path.

Fixes TanStack#421
@valeriudev valeriudev force-pushed the fix/issue-421-client-tool-status branch from e274294 to a9f1eb9 Compare June 11, 2026 18:46
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.

useChat status is still streaming after ClientToolCall triggers with RUN_FINISHED

1 participant