Skip to content

ToolCallManager can run the same tool call twice on a repeat TOOL_CALL_START #1187

Description

@AlemTuzlak

TanStack AI version

@tanstack/ai on feat/ag-ui-metadata-compliance (PR #1174)

Framework/Library version

N/A (engine, @tanstack/ai)

Describe the bug and the steps to reproduce it

ToolCallManager.addToolCallStartEvent keys the internal map by index (event.index ?? map.size). AG-UI spec TOOL_CALL_START has no index. First-party adapters still send index on the adapter yield, and they emit START only once (!started after id and name exist).

If a producer sends TOOL_CALL_START twice for the same toolCallId:

  1. Same id, same index: the second START overwrites the slot and resets arguments to ''. Any TOOL_CALL_ARGS already stored are lost.
  2. Same id, missing or different index: the second START inserts a second map row. getToolCalls() returns the id twice, and the engine can run the tool twice.

First-party adapters do not hit this. It is a custom-server / malformed-stream case.

We reverted an in-PR guard (skip a second START with the same id) out of #1174 so that PR stays on the AG-UI metadata work.

A later fix can skip a repeat START for an already-tracked toolCallId, or key the map by id.

Minimal sequence:

manager.addToolCallStartEvent({
  type: "TOOL_CALL_START",
  toolCallId: "call_1",
  toolCallName: "ask_user",
  index: 0,
})
manager.addToolCallArgsEvent({
  type: "TOOL_CALL_ARGS",
  toolCallId: "call_1",
  delta: '{"question":"Which option?"}',
})
manager.addToolCallStartEvent({
  type: "TOOL_CALL_START",
  toolCallId: "call_1",
  toolCallName: "ask_user",
  index: 0,
})
// arguments are now ""

Related: #1174

Your Minimal, Reproducible Example - (Sandbox Highly Recommended)

#1174ToolCallManager.addToolCallStartEvent in packages/ai/src/activities/chat/tools/tool-calls.ts. Use the snippet above in packages/ai/tests/tool-call-manager.test.ts.

Do you intend to try to help solve this bug with your own PR?

Maybe, I'll investigate and start debugging

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    has-prAn open PR references this issuewaiting-on: maintainerThe ball is in the maintainers’ court

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions