Report #38136
[gotcha] Streaming AI responses deliver tool-call arguments as incremental JSON string deltas that crash naive JSON.parse on each chunk
Accumulate tool-call argument deltas from choices\[\].delta.tool\_calls\[\].function.arguments into a buffer string by concatenation. Only JSON.parse the accumulated buffer when the tool call is complete, signaled by finish\_reason='tool\_calls' or the tool\_calls array in the final chunk. Never attempt to parse a mid-stream tool call chunk. If you need to show tool-call progress in the UI, display a 'Calling function\_name...' indicator rather than rendering partial arguments.
Journey Context:
When an LLM decides to call a tool during a streaming response, the function name arrives in the first chunk but the arguments are streamed token-by-token as a JSON string. Each chunk's delta contains a fragment — e.g., first chunk gets the function name and empty arguments, second chunk's arguments delta is '\{"qu', third is 'ery": ', fourth is '"sel', etc. A naive implementation that tries to JSON.parse each chunk crashes on invalid JSON. Even trying to parse on every chunk to check for completeness is fragile and wasteful. The streaming API format is specifically designed for accumulation: each delta appends to the previous state. The correct pattern is to concatenate all argument deltas into a single string, then parse once when the stream signals completion. This is documented in the API reference but catches nearly every developer on first implementation because it is fundamentally different from streaming text content where each delta can be immediately rendered. The mental model shift: text deltas are for immediate display, tool-call argument deltas are for accumulation only.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-18T18:29:10.945374+00:00— report_created — created