Streaming, Errors & Resilience

Tool Input Streaming: Buffering partial_json Fragments and When to Enable eager_input_streaming

Text streams as text, but tool arguments stream as shards of a JSON document that is only valid once it's complete. Parsing too early is the classic bug; knowing when the fragments are safe to read is the whole game.

Claude 3P 101 · Updated July 2026 · Unofficial guide

When Claude decides to call one of your tools during a streamed response, the tool call occupies its own content block, delivered like every block as content_block_start, a series of content_block_delta events, and a content_block_stop. The deltas, though, are not text: they are input_json_delta events, each carrying a partial_json string — a fragment of the JSON object that will become the tool's input. Anthropic's streaming documentation gives the contract in one sentence: accumulate the partial_json strings and parse the result after content_block_stop.

Why fragments, and why you must wait

A fragment is exactly what it sounds like: a substring of the serialized JSON, with no promise of being independently parseable. Concatenated in order, the fragments form the complete input object; individually, one may end mid-key or mid-value. The robust consumer is therefore a plain string buffer per tool-use block:

import json
from anthropic import Anthropic

client = Anthropic()
buf = ""
with client.messages.stream(model="claude-opus-4-8", max_tokens=4096,
                            tools=tools, messages=messages) as stream:
    for event in stream:
        if event.type == "content_block_delta" and \
           event.delta.type == "input_json_delta":
            buf += event.delta.partial_json
        elif event.type == "content_block_stop":
            tool_input = json.loads(buf)   # only now is this valid JSON

(The SDKs' higher-level helpers do this accumulation for you; the manual version is shown so the contract is visible.) Two documented behaviors are worth engineering around. First, current models emit one complete key/value pair at a time, so there may be noticeable delays between delta events — a quiet wire while a long parameter value is being generated is normal, and it's part of why ping events exist. Second, because the block's index ties every delta to its position in the final content array, multiple tool calls in one response each get their own buffer — key your accumulation by block index, not by "the current tool."

When waiting is too late: eager_input_streaming

For most integrations, parse-at-stop is exactly right: you can't execute half a tool call anyway. But some experiences genuinely benefit from seeing parameters before the object completes — think of a UI previewing the file path an agent is about to write, or a long free-text argument you want to render as it's generated rather than after a multi-second silence. For those cases the API offers fine-grained tool streaming, enabled per tool with eager_input_streaming, which delivers parameter values earlier rather than holding delivery to whole key/value boundaries.

The trade-off is that eagerness moves the incompleteness problem into your code: earlier delivery means acting on parameters before the surrounding object has closed. Sensible uses are display, logging, and precomputation — things that are harmless if the final parse changes the picture. The actual execution of a tool should still gate on the completed, parsed input after content_block_stop.

Rule of thumb: buffer by block index, parse only at content_block_stop, and turn on eager_input_streaming per tool only where a human is watching the parameters arrive. Never dispatch a side-effecting tool from a partial buffer.

Validation belongs after the parse

A successfully parsed buffer is well-formed JSON, not necessarily a valid input for your tool. If malformed or schema-violating tool inputs would hurt, the API's strict tool use feature ("strict": true on the tool definition) guarantees schema-valid tool names and inputs at generation time — a stronger promise than any client-side repair. One caveat carries over from the refusal article's theme of body-level failures: a response truncated by max_tokens can leave the last tool-use block incomplete, so a JSON parse error after a stop_reason of "max_tokens" means "raise the ceiling and retry," not "repair the JSON."

Where to go next

The tool-use loop end to end is covered in streaming with tools and tool use 101; schema-level guarantees in strict tool use; and Python-side orchestration in the Python tool runner.

Sources