Error: API Error: 400 {"type":"error","error":{"type":"invalid_request_error","message":"messages.156: Did not find 1 `tool_result` block(s) at the beginning of this message. Messages following `tool_use` blocks must begin with a matching number of `tool_result` blocks."}}
This is request validation, not inference. The message array you sent was
checked and rejected before any model ran, which rules out the classes people
check first: not your API key (that is a 401), not billing, not a quota, not
capacity, and not the model failing at the task. A 400 with
invalid_request_error means a format or content problem with what you sent.
The useful part is that this particular one names the exact position of the
problem, and most readers throw that away.
Read the three pieces of data in the message
messages.156 is an index into the array in the request body you just sent. It
is not a turn number in any interface, not a line in a log, and not stable
across requests if anything between you and the API reshapes the history. It
points at a user message — the one that was supposed to open with tool
results.
The offending tool_use blocks are therefore in the message before it, at
index 155. That subtraction is the whole navigation problem solved: you do not
have to scan the conversation, you have a coordinate.
Did not find 1 tool_result block(s) names how many results were required. One
means the preceding assistant message made a single tool call; a larger number
means it fanned out into a parallel batch, and the two situations have different
causes. A single unanswered call usually comes from a loop bug; a batch usually
comes from something that tore the batch down — an interrupt, a crash, a
cancelled subprocess.
And at the beginning of this message is a requirement about order, not
just presence. This is the sentence that gets skimmed, and it is the cause in a
large share of self-built agent loops: the results are in the message, they are
simply not first. If your loop appends the user’s new instruction and then the
tool results, the array contains everything it needs and is still rejected.
One more signal, from the prefix rather than the body: Error: API Error: 400
followed by the provider’s raw JSON means your client passed the response
through untouched. That is good news. You are reading the validator’s own words,
with the index intact — a client that summarizes this into its own phrasing
usually drops the index, and then you are guessing where to look.
Is retrying useful?
No. The array is fixed the moment your client serializes it, and a deterministic validator returns the same verdict for the same bytes.
The official SDKs retry transient failures — connection errors, rate limits,
5xx — with exponential backoff, twice by default. A 400 is deliberately
excluded, and that is correct rather than a gap: waiting does not reorder an
array.
Retry once if you want the proof; a second identical rejection at the same index
confirms a deterministic failure and everything below applies. Then exclude
400 from any retry loop you wrote yourself, because here the retry is
expensive in an unusual way — each attempt re-uploads the entire conversation
just to be rejected at the same position.
Two things that look like retrying and are not: switching models cannot help, because validation runs before a model is selected, and continuing the conversation actively makes it worse, since typing a new instruction appends another user message that does not begin with tool results.
The validator stops at the first violation
Assume it did. The message names one index, and one index is all you get — a repaired array can be rejected again at a higher index on the very next attempt.
This changes the repair strategy in a way that is not obvious from the message and costs people a long afternoon otherwise. Do not fix index 156, resend, read the next index, fix that, and resend. If the cause was systematic — a loop that orders blocks wrongly, a summarizer that drops user messages, a crash that severed several batches — the array contains several violations, and fixing them one round trip at a time turns a single bug into a dozen full request replays.
Instead, validate the whole array locally before sending anything. Walk it once:
for every assistant message, collect the tool_use ids; then check that the
next message is a user message whose leading blocks are tool_result blocks
matching those ids in count and in id. Every place that fails is a place the API
will fail. This check is a few lines of code and it turns an interactive
debugging session into a single pass.
Telling the causes apart
- Results present but not first. Dump the content array of message 156 and
look at the order of block types. If a
textblock precedes thetool_resultblocks, this is your cause, it is systematic, and it affects every tool turn you have ever built. - Count mismatch in a parallel batch. Message 155 has three
tool_useblocks and message 156 has twotool_resultblocks. Something abandoned part of the batch. In a CLI, check whether the failure began on the turn right after an interrupt or a crash. - Ids do not correspond. Same counts, different
tool_use_idvalues — results generated for calls other than the ones that were made. This is a bookkeeping bug in a loop that matches results to calls by position rather than by id, and it survives every reordering fix you try. - The index is not where you expect it in a long conversation. If your history has far more than 157 messages and the failure names 156, something is reshaping the array: a sliding window, a summarizer, a gateway. The discrepancy is itself the finding. Conversely, if you run a sliding window on purpose, an index that changes every attempt while naming the same underlying break is expected and not a second bug.
- It began right after compaction or summarization. A rewrite whose boundary
falls between an assistant
tool_useand the user message carrying its results severs the pair with nobody having interrupted anything. The tell is a compaction step as the last thing logged before the first failure.
Fix by cause
- Ordering — put every
tool_resultblock at the head of the user message and append the user’s own text after them. One user message may carry both; the results simply have to come first. - Missing results in a batch — synthesize the ones that are absent: one
tool_resultper unanswered id, marked as an error result saying the call was cancelled. This preserves the turn. The alternative is dropping the trailing assistant message entirely, which restores the invariant but loses the turn. - Id mismatch — key results to
tool_use_id, never to array position. Parallel calls complete out of order, so position is wrong the moment more than one call is in flight. - You are in a CLI rather than your own loop — do not hand-edit. Rewind to a
checkpoint before the assistant turn at index 155. Claude Code stores the
transcript as JSONL under
~/.claude/projects/<project>/<session-id>.jsonl, so you can read the array to confirm which turn that is, but repair it through the rewind command rather than by editing the file. - Do not repair by deleting the dangling
tool_useblock from the latest assistant message. With extended thinking on, that message’s thinking blocks must go back exactly as they arrived, and the edit trips a different 400 about modified thinking blocks. Truncating history is allowed; doctoring the most recent assistant turn is not.
Confirming the array validates
Do not re-run the task that broke it. A task that fans out into parallel tools cannot distinguish “the history is repaired” from “the history is still broken but nothing got interrupted this time”.
Send one message that triggers no tools — a plain question — carrying the
full history. If it returns normally, the array validated end to end, because
validation covers the whole array and not just the tail. If it returns a 400
at a different index, your repair worked and you have found the second
violation; that transition is exactly what the local walk above prevents.
Then run one task that fires a single tool, and only after that one that fans out. If you added the local validator, the stronger confirmation is that it runs on every outgoing request and has stopped firing — a check that never rejects anything is either fixed or broken, so assert it against a deliberately malformed array once to be sure it can still fail.
Related errors
If you are in Claude Code rather than your own loop, the same break usually
arrives wrapped as
a 400 naming tool use concurrency with an instruction to run /rewind
— same cause, but without the index, which is why that page has to teach you to
rewind further than feels necessary and this one does not. If the call never
became a valid tool_use in the first place,
a tool call that could not be parsed
is the upstream event that leaves this array behind, and it is worth reading if
your failures start there. The
AI coding error triage tool routes this family
by whether the stored history is still valid — the question that decides between
repairing an array and simply re-running a turn.