Skip to content

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 text block precedes the tool_result blocks, 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_use blocks and message 156 has two tool_result blocks. 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_id values — 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_use and 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_result block 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_result per 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_use block 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.

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.