LLM Message Array Linter

Paste the messages array from the request that just returned a 400 and get the exact block index that broke it, plus a truncation map showing every index where the conversation can legally be cut.

Anthropic Messages API only. Runs entirely in your browser: no upload, no network request, no API key.

1. Paste the messages array

Accepts a bare JSON array, or a whole request body with a messages key (the rest of the body is ignored).

What gets checked, and what a "span" means

The linter walks the transcript as tool_use to tool_result spans rather than as a flat list of messages. A span opens at an assistant message containing one or more tool_use blocks and closes at the user message that carries a tool_result for every one of those ids. The atomic unit therefore crosses a message boundary by definition, and it fans out under parallel tool use.

  • Orphaned tool_use - a tool_use id with no matching tool_result.
  • Orphaned tool_result - a tool_use_id that matches no earlier tool_use.
  • Partially answered fan-out - N parallel tool_use blocks, fewer than N results. Each block passes a naive per-block validator; the request still fails.
  • Misplaced results - results that are not in the message immediately after their tool_use, or split across several user messages.
  • Ordering - text placed before tool_result in the same user message.
  • Thinking blocks - reconstructed or unsigned thinking / redacted_thinking blocks on the most recent assistant message.
  • Trailing assistant turn - an array that ends on an assistant message is a prefill.

Every rule above is quoted, with its source URL and the date it was checked, in the Receipts section further down.

2. Result

Nothing analyzed yet.

Load a sample or paste your own array, then press Analyze.

3. Receipts

Everything in section 2 is computed from the array you pasted. Nothing below feeds into it.

The blocks below are dated quotes from vendor documentation, reproduced so you can re-check them yourself. Vendor docs change. If a quote here goes stale, that does not make the computation above wrong - it means this page needs a new quote.

Results must immediately follow their call, with nothing in between

Anthropic, Handle tool calls. Checked .

"Tool result blocks must immediately follow their corresponding tool use blocks in the message history. You cannot include any messages between the assistant's tool use message and the user's tool result message."

The same page names the error string you get when this is violated: "If you receive an error like tool_use ids were found without tool_result blocks immediately after, check that your tool results are formatted correctly." Source

Results come first in the content array, before any text

Anthropic, Handle tool calls. Checked .

"In the user message containing tool results, the tool_result blocks must come FIRST in the content array. Any text must come AFTER all tool results."
"If the assistant turn also called a server tool that has no result block yet, the user message must contain only tool_result blocks. Text after the results ends the turn early; for a server tool Claude called directly, the request then fails with a 400 error that names the unresolved server tool."

The page prints a JSON example labelled "For example, this will cause a 400 error:", with a text block preceding the tool_result, and marks the reverse order correct. Source

A parallel fan-out is answered all at once, or not at all

Anthropic, Parallel tool use. Checked .

"Whichever strategy you use, return one tool_result for each tool_use block, all together in the next user message."
"If you choose not to run a particular call (for example, because you ran the batch sequentially and an earlier call failed), still return a tool_result for it with is_error: true and a brief explanation."

The troubleshooting section contrasts "Wrong: a separate user message for each tool result" with "Correct: all tool results together in a single user message", and says the wrong shape "teaches" the model to avoid parallel calls. This linter reports a split fan-out separately from a missing one, because the two fail differently. Source

Thinking blocks: an API rule, on the most recent assistant message, not an opt-in

Anthropic, Errors and Thinking. Checked .

"If the most recent assistant message contains thinking or redacted_thinking blocks that were edited, reordered, filtered out, or reconstructed before being sent back to the API, the request returns a 400 invalid_request_error."
"thinking or redacted_thinking blocks in the latest assistant message cannot be modified. These blocks must remain as they were in the original response."
"With tool use, every thinking and redacted_thinking block from the assistant turn must be passed back exactly as received, including blocks whose thinking field is empty."

Three things follow, and each corrects a common mis-statement of this rule. It is an API rule returning 400 invalid_request_error, not an SDK convention. It binds on the most recent assistant message, not on every assistant turn. And it is not conditional on opting into extended thinking: the Thinking page states "On Claude Opus 5, Claude Sonnet 5, Claude Fable 5, Claude Mythos 5, and Claude Mythos Preview, thinking is already on: no configuration needed", so the blocks arrive whether or not you asked for them. Errors - Thinking

Whether prior turns' thinking stays in context is model-dependent

Anthropic, Thinking. Checked .

"Keep all prior turns: Claude Opus 4.5 and later Opus models, Claude Sonnet 4.6 and later Sonnet models, Claude Fable 5, Claude Mythos 5, and Claude Mythos Preview."
"Keep the last turn only: earlier Opus and Sonnet models, and all Haiku models through Claude Haiku 4.5. When you pass older thinking blocks back, the API strips them automatically. You don't need to remove them yourself."
"On models that keep all prior turns, previous thinking blocks remain in context, count toward the window, and are billed as input tokens like the rest of the conversation history. On models that keep only the last turn, the API strips older thinking blocks automatically when you pass them back, so they don't consume window space or input tokens."

This is why the linter never tells you to strip old thinking blocks. On keep-all models they are live context you are paying for; on last-turn-only models the API drops them for you. Either way, hand-pruning is what breaks the most recent turn. Source

Ending on an assistant message is a prefill

Anthropic, Errors. Checked .

"This model does not support assistant message prefill. The conversation must end with a user message."

Quoted from the 400 body shown on the errors page, under "Prefill not supported", which scopes it to "Claude 4.6 and later models and Claude Mythos Preview". Source

The user-role head rule: empirically observed, undocumented

Third-party bug tracker, not vendor documentation. Checked .

A widely repeated claim is that the surviving head of a trimmed conversation must begin with a user turn. This linter does not treat that as a legality rule, because it appears in no current primary Anthropic source. Checked on : it is not in the Messages API messages parameter description, not on the errors page, and not in the Amazon Bedrock mirror of the Messages API.

What does exist is a runtime error string, observed and pasted into a public bug report on 2024-03-16 against api.anthropic.com with HTTP 400 and invalid_request_error:

messages: first message must use the "user" role

Reported in lobehub/lobehub issue 1601, opened 2024-03-16. The reporter's summary: "Seems like Anthropic API isn't really happy when you're sending first message under the assistant role." Treat this as behavior someone hit, not as a contract. Source

The truncation map therefore reports head role as a separate advisory column, never as the SAFE / UNSAFE verdict. Note also that this rule and the documented merge rule are orthogonal, not contradictory: "Consecutive user or assistant turns in your request will be combined into a single turn" is about collapsing adjacent same-role turns, and says nothing about which role may lead. Messages API parameter description

4. What this tool does not do

  • It does not validate tool input against your input_schema. A schema-invalid input is a different failure from a structurally broken transcript.
  • It does not count tokens or decide what your context budget should be. It tells you where a cut is structurally safe, not where a cut is a good idea.
  • It cannot tell you whether a missing thinking block was filtered out by your code or was never generated. Adaptive thinking can skip thinking on a simple request, so an absent block is reported as a suspect, never as a verdict.
  • It does not know your model. Rules whose scope is model-dependent are labelled as such rather than applied blindly.
  • It does not model server tools. The results-first rule is checked for client tool round trips only; if the same assistant turn also left a server_tool_use unresolved, the documentation says that user message must carry nothing but tool_result blocks, and this linter will not catch trailing text in that narrower case.
  • It covers the Anthropic Messages API only. OpenAI's Chat Completions uses separate role: "tool" messages and the Responses API uses flat function_call_output items keyed by call_id; neither has the fan-out atomicity constraint that this tool exists to check, so there is deliberately no shape auto-detect here.
  • A clean report means the transcript is structurally well-formed. It is not a promise that the API will accept it.