A gateway that labels two distinct streamed tool calls with the same tool_calls[].index gets them merged into one. The arguments are concatenated into invalid JSON, the second call's id and name are dropped, and nothing errors.
in: {index:0, id:"call_a", name:"read_file", args:'{"path":"a"}'}
{index:0, id:"call_b", name:"read_file", args:'{"path":"b"}'}
out: [{"toolCallId":"call_a","input":"{\"path\":\"a\"}{\"path\":\"b\"}"}]
processExistingToolCall in @ai-sdk/provider-utils only appends to arguments; it ignores the id and function.name on every delta after the first for a given index.
This is worse than the crash in #1967. The agent proceeds believing it called one tool with garbage arguments, and the second tool never runs, with no signal anywhere.
Reach. 46 providers in provider-registry.ts use the openai-compatible runtime adapter, including ollama and ollama-cloud, both status: 'ready'. vercel/ai#14277 reports that Ollama labels every tool call in a turn with index: 0 (verified there against Ollama 0.20.2/0.20.4 with qwen3-coder:30b and others, 100% failure on multi-tool turns). I reproduced the merge at the protocol layer through the real provider stack, but I have not run it end to end against a live Ollama, so that half rests on the upstream report.
Secondary case, same root. When deltas carry no index at all and a call spans more than one chunk, the ?? this.toolCalls.length fallback picks a fresh slot for the continuation and it throws Expected 'id' to be a string. No known maka path hits this today, since Google uses the native adapter, but a relay could.
Not caused by #1971. Output is byte-identical with and without that patch; it fixes a crash from sparse indices and deliberately does not widen into this. patches/README.md records both cases.
Why this isn't a one-line fix.index says which call a delta belongs to, but identity actually lives in toolCallDelta.id. The tracker keys off neither consistently, so any real fix rekeys it on id and decides what to do when a gateway violates the protocol: two distinct calls, or an explicit protocol error, but never a silent merge. That is upstream's call to make. Tracking here so it isn't rediscovered from a user report.
Upstream: vercel/ai#18333, vercel/ai#14277.
A gateway that labels two distinct streamed tool calls with the same
tool_calls[].indexgets them merged into one. The arguments are concatenated into invalid JSON, the second call'sidandnameare dropped, and nothing errors.processExistingToolCallin@ai-sdk/provider-utilsonly appends toarguments; it ignores theidandfunction.nameon every delta after the first for a given index.This is worse than the crash in #1967. The agent proceeds believing it called one tool with garbage arguments, and the second tool never runs, with no signal anywhere.
Reach. 46 providers in
provider-registry.tsuse theopenai-compatibleruntime adapter, includingollamaandollama-cloud, bothstatus: 'ready'. vercel/ai#14277 reports that Ollama labels every tool call in a turn withindex: 0(verified there against Ollama 0.20.2/0.20.4 with qwen3-coder:30b and others, 100% failure on multi-tool turns). I reproduced the merge at the protocol layer through the real provider stack, but I have not run it end to end against a live Ollama, so that half rests on the upstream report.Secondary case, same root. When deltas carry no
indexat all and a call spans more than one chunk, the?? this.toolCalls.lengthfallback picks a fresh slot for the continuation and it throwsExpected 'id' to be a string.No known maka path hits this today, since Google uses the native adapter, but a relay could.Not caused by #1971. Output is byte-identical with and without that patch; it fixes a crash from sparse indices and deliberately does not widen into this.
patches/README.mdrecords both cases.Why this isn't a one-line fix.
indexsays which call a delta belongs to, but identity actually lives intoolCallDelta.id. The tracker keys off neither consistently, so any real fix rekeys it onidand decides what to do when a gateway violates the protocol: two distinct calls, or an explicit protocol error, but never a silent merge. That is upstream's call to make. Tracking here so it isn't rediscovered from a user report.Upstream: vercel/ai#18333, vercel/ai#14277.