-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Description
Description
Summary
When a model calls a tool that doesn't exist in the tools object (e.g. a hallucinated tool name), processUIMessageStream creates two message parts for the same toolCallId — one static (tool-{toolName}) and one dynamic (dynamic-tool). This causes downstream failures when the conversation is continued, since the model API rejects requests containing two tool results for the same tool call ID.
Steps to Reproduce
- Configure streamText with a set of tools.
- Have the model call a tool name that is not in the tools object.
- The stream emits tool-input-start → tool-input-delta → tool-input-error → tool-output-error for the same toolCallId.
- Observe that the resulting assistant message contains two parts with the same toolCallId.
Root Cause
In process-ui-message-stream.ts:
- tool-input-start arrives without dynamic set (the tool isn't in the tools object, so
tool?.type === 'dynamic'isundefined). Sincechunk.dynamicis falsy,updateToolPartis called, which creates a static part (e.g.type: "tool-some-nonexistent-tool-name"). - tool-input-error arrives with dynamic: true (set by
parseToolCall's catch block forNoSuchToolError). The handler branches onchunk.dynamicand callsupdateDynamicToolPart, which only searches for parts withtype === 'dynamic-tool'. It doesn't find the existing static part, so it creates a second part withtype: "dynamic-tool".
Expected Behavior
Only one message part should exist per toolCallId. When tool-input-error arrives for a toolCallId that already has a part (regardless of whether it's static or dynamic), it should update that existing part rather than creating a new one.
Actual Behavior
Two parts are created for the same toolCallId:
{ type: "tool-{toolName}", toolCallId: "...", state: "input-streaming", ... }
{ type: "dynamic-tool", toolCallId: "...", state: "output-error", ... }
Impact
When these messages are persisted and sent in a subsequent request, convertToModelMessages produces two tool results for the same toolCallId, which the Anthropic API (and likely others) rejects as invalid.
AI SDK Version
- ai 6.0.49
Code of Conduct
- I agree to follow this project's Code of Conduct