Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Failed server tool call is saved without `error`, so it reloads as complete

Open
#1,470 0 comments 0 reactions 1 assignee View on GitHub

@tombeckenham is already working on this.

Since Sep 23, 2026.

Assessment

This issue has not been assessed yet.

Description

has-pr waiting-on: maintainer
TanStack AI version

@tanstack/ai 0.58.0. Also 0.55.0. The code is the same on main.

Framework/Library version

Framework-agnostic (Node 24)

Describe the bug and the steps to reproduce it

A server tool call fails when the handler throws, when the input fails its schema, or when the tool does not exist. chat() streams the TOOL_CALL_RESULT with metadata.tanstack.state: 'output-error'. The client then shows a tool-result part with state: 'error' and error, as the client tools docs describe.

But the tool message that chat() adds to its message history has no error:

{ role: 'tool', content: '{"error":"lookup is down"}', toolCallId: 'call_1' }

Middleware reads that history in onFinish, and @tanstack/ai-persistence saves it. On a reload, modelMessagesToUIMessages gives the tool-result part state: 'error' only when msg.error is set (messages.ts#L1128-L1141). As a result, the part comes back with state: 'complete' and no error. The UI shows the call as failed while the run streams, and as successful after a reload.

buildToolResultChunks uses result.state to tag the chunk, but not to build the message (index.ts#L3157-L3185).

Steps:

  1. In the linked repo, run npm install && npm run repro. No API key is necessary. A scripted adapter makes the tool call.
  2. For each of the three failures, the script prints the streamed metadata, the saved tool message, and the rebuilt tool-result part. This is the output for the handler that throws:
## handler throws
1. streamed TOOL_CALL_RESULT metadata: { tanstack: { state: 'output-error' } }
2. saved tool message: {
  role: 'tool',
  content: '{"error":"lookup is down"}',
  toolCallId: 'call_1'
}
3. rebuilt tool-result part: { state: 'complete', error: undefined }

Expected: the saved tool message has error, and the rebuilt part is { state: 'error', error: 'lookup is down' }. That is the same state that the stream processor sets live (processor.ts#L1655-L1682).

When result.state === 'output-error', set error on that message. This fixes all three cases in the repro. A PR follows.

Related: #1386 and #1401 are about denied and cancelled calls. #1401 adds toolResultOutcome to their metadata. It does not mark a plain failure. I ran a test of the three cases against the head of #1401, and all three still rebuild as complete. Both change the same block of code.

Your Minimal, Reproducible Example - (Sandbox Highly Recommended)

https://github.com/aayushbtw/tanstack-ai-tool-error-repro

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
  • 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.
Dominant language
TypeScript
Stars
3.1k
Forks
331
Avg merge
1d 10h
Merged PRs (30d)
130

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from TanStack/ai

All issues in TanStack/ai

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.