Skip to content

fix(ai): prevent duplicate tool parts in tool-input-available handler#13868

Open
pawaca wants to merge 1 commit intovercel:mainfrom
pawaca:fix/duplicate-tool-parts-input-available
Open

fix(ai): prevent duplicate tool parts in tool-input-available handler#13868
pawaca wants to merge 1 commit intovercel:mainfrom
pawaca:fix/duplicate-tool-parts-input-available

Conversation

@pawaca
Copy link

@pawaca pawaca commented Mar 27, 2026

Summary

Follow-up to #12774 which fixed the same duplicate-part bug in the tool-input-error handler.

The tool-input-available handler has the same bug: it uses chunk.dynamic directly to decide whether to call updateDynamicToolPart or updateToolPart, without first checking if there's already an existing part for the same toolCallId. This causes a duplicate when:

  1. tool-input-start creates a static tool-{name} part (dynamic is undefined/false)
  2. tool-input-available arrives with dynamic: true (from parseToolCall's NoSuchToolError catch)
  3. updateDynamicToolPart doesn't find an existing dynamic-tool part, so it creates a new one — resulting in two parts for the same toolCallId

Real-world impact: sessions become permanently broken when the model calls a non-existent tool, because the duplicate tool_use IDs cause API rejection on every subsequent request.

The fix applies the same pattern already used in the tool-input-error case: look up the existing part by toolCallId and use its type to decide the update path, falling back to chunk.dynamic only when no existing part is found.

Closes #12772

Test plan

  • Added regression test tool input available with dynamic flag mismatch (mirrors the existing tool input error with dynamic flag mismatch test from fix(ai): Don't create duplicate tool parts when models call non-existent tools #12774)
  • Test asserts exactly one part exists for the toolCallId (no duplicate)
  • Test asserts the static tool type from tool-input-start is preserved
  • All 97 existing tests in process-ui-message-stream.test.ts continue to pass

Follow-up to vercel#12774: apply the same existing-part lookup pattern to
the tool-input-available case so that a dynamic flag mismatch between
tool-input-start and tool-input-available does not create a duplicate
part.

Closes vercel#12772
@tigent tigent bot added ai/ui anything UI related bug Something isn't working as documented labels Mar 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai/ui anything UI related bug Something isn't working as documented

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Two tool parts with the same toolCallId created when the model calls a non-existent tool name

1 participant