DEV Community

Multigrid
Multigrid

Posted on • Originally published at multigrid.ai

disable_parallel_tool_use in the Claude API

By default Claude may emit several tool_use blocks in one turn and expect you to run them all before replying. disable_parallel_tool_use turns that off. It is a field on tool_choice, not a top-level parameter, and what it guarantees depends on which tool_choice type it sits inside.

The default is parallel

Given tools for get_weather and get_time and a question that needs both, Claude will typically call both in a single turn:

{
  "role": "assistant",
  "content": [
    { "type": "tool_use", "id": "toolu_01A...", "name": "get_weather",
      "input": { "city": "Lisbon" } },
    { "type": "tool_use", "id": "toolu_01B...", "name": "get_time",
      "input": { "city": "Lisbon" } }
  ],
  "stop_reason": "tool_use"
}
Enter fullscreen mode Exit fullscreen mode

This is the behaviour you want most of the time. Two independent lookups issued together cost one model round trip instead of two, and a model round trip is by far the most expensive part of an agent loop — it re-sends the entire conversation and pays prefill on all of it. Your side of the contract is to run both and return both tool_result blocks in the single user message that follows.

It becomes a problem when the calls are not independent. If the second call’s arguments should depend on the first call’s result — look up an account, then charge it — a parallel pair means the model guessed the second set of arguments without the information it needed. The model has no way to express “wait for that one” within a turn.

It is worth being clear that the model is not doing anything wrong when that happens. It emitted two calls because both looked answerable from what it had; the dependency lived in your system and was never stated anywhere the model could see it. That framing points at the fix — either make the dependency visible, or make parallelism impossible — and this flag is only the second of the two.

Where the flag lives

It is a boolean inside the tool_choice object, defaulting to false:

{
  "model": "claude-sonnet-4-20250514",
  "max_tokens": 1024,
  "tools": [ /* ... */ ],
  "tool_choice": { "type": "auto", "disable_parallel_tool_use": true },
  "messages": [ { "role": "user", "content": "Weather and time in Lisbon?" } ]
}
Enter fullscreen mode Exit fullscreen mode

With it set, the same question produces one call per turn:

{
  "role": "assistant",
  "content": [
    { "type": "tool_use", "id": "toolu_01A...", "name": "get_weather",
      "input": { "city": "Lisbon" } }
  ],
  "stop_reason": "tool_use"
}

// You return the weather result; the NEXT turn asks for get_time.
Enter fullscreen mode Exit fullscreen mode

Note the cost this made explicit: answering the question now takes three model calls rather than two, and the conversation grows between each. That is the trade — correctness of sequencing, paid for in round trips and input tokens.

What it means under each tool_choice type

tool_choice has four types, and the flag composes with three of them differently. This is the distinction to get right:

  • {"type": "auto"} — the model decides whether to use a tool at all. With disable_parallel_tool_use: true it will output at most one tool call. Zero is still possible: the model may answer in text and end the turn with stop_reason: "end_turn".
  • {"type": "any"} — the model must use one of the tools, but chooses which. With the flag it will output exactly one tool call.
  • {"type": "tool", "name": "..."} — the named tool must be used. With the flag it is called exactly once, which is the thing worth knowing: without it, a forced tool can still be called more than once in a turn. This is the combination behind reliable structured output; see forcing a specific tool with tool_choice.
  • {"type": "none"} — no tools may be used, so the flag is irrelevant.

“At most one” versus “exactly one” is the whole difference between auto and any here, and code that assumes a tool call is present after setting the flag under auto will occasionally get a plain text answer instead. Branch on stop_reason, not on the flag you set.

Forcing tool use has a documented interaction with extended thinking: the thinking-enabled path does not support the forced modes in the same way as the default path. If you are combining thinking with tool_choice, check Anthropic’s tool use documentation for the current constraints rather than assuming the combination is free.

When to turn it on

Cases where forcing one call per turn is the right call:

  • Dependent steps. The second call’s arguments come from the first call’s result. Parallel here does not produce a slow answer, it produces a wrong one.
  • Side effects. Anything that writes, charges, sends or deletes. One action per turn means one thing to review, one thing to confirm, and one thing to undo.
  • Rate-limited or expensive tools. Serialising is a crude throttle, but it is a throttle the model cannot get around.
  • Single-shot structured output. With type: "tool" and the flag set, you get exactly one call with schema-shaped arguments — a clean extraction with no second block to reconcile.
  • Debugging an agent loop. One call per turn makes a trace readable. It is worth turning on while diagnosing and off again afterwards.

Cases where it is not: read-only lookups over independent entities, fan-out retrieval across several sources, and anything latency-sensitive where the calls genuinely do not interact. Turning it on globally because one route needed it is a common and quietly expensive mistake — it is a per-request field, and it should be set per route.

Two operational notes. The flag constrains the number of tool_use blocks; it does not remove the accompanying text block, which can still appear alongside the single call — see text and tool calls in the same turn. And it is Anthropic-specific: the equivalent on OpenAI-shaped APIs is a top-level parallel_tool_calls: false, in a different place in the request with a different default, so a translation layer that maps tool_choice alone will silently drop this setting.

Sequencing without the flag

The flag is blunt: it applies to the whole request, and it costs a round trip per step whether or not the steps were actually dependent. Three alternatives are worth knowing, because they are often a better fit than turning parallelism off wholesale.

  • Encode the dependency in the tool schema. If charge_account requires an account_id that only lookup_account can produce, and its description says so, the model has no plausible way to call both in one turn — it does not have the argument yet. A required opaque identifier is a stronger constraint than a request flag, because it makes the wrong behaviour unrepresentable rather than merely disallowed.
  • Vary tool_choice per turn. It is a per-request field, so a loop can hold the flag off for the read-only phase and switch it on — or force a specific tool — for the step that writes. This gives you fan-out where fan-out is safe and strict sequencing where it is not, within one conversation.
  • Serialise on your side. Nothing obliges you to run parallel calls in parallel. You may execute them in array order, or run the first and return a tool_result with is_error: true for the rest explaining that they must be requested again — the model will re-issue them with the first result now in context. This is more code, and it is the only option that lets you decide per call rather than per request.

Whichever you choose, one thing does not change: the number of tool_result blocks in the following user message must match the number of tool_use blocks in the assistant turn. Deciding not to run a call is not the same as being allowed to omit its result.

Related

Top comments (0)