A messages array is a contract with hidden ordering rules: tool responses must directly follow the assistant message that declared them, every declared tool_call_id must be answered before the conversation moves on, and Anthropic additionally requires user and assistant to alternate with system moved to a top-level parameter. When code assembles the array dynamically - trimming history, retrying calls, merging turns - these invariants are easy to break, and the API error message quotes the rule without telling you which message violated it. This tool walks the array with the same pairing logic and points at the exact index.
OpenAI Chat Completions and Anthropic Messages disagree on fundamentals: which roles exist, where system instructions live, and how tool calls pair with their results (top-level tool_calls and tool messages versus tool_use and tool_result content blocks). Switch the profile and the same input is re-judged instantly - useful when migrating a conversation log from one provider to the other, because the diagnosis list becomes your migration checklist.
Everything is parsed and diagnosed locally in your browser. Nothing you paste is uploaded, logged or recorded - conversation logs routinely contain credentials, customer data and internal prompts, so there is deliberately no server side and no analytics on your input.
Common questions and answers about this topic.
The structural 400s: messages with role tool must respond to a message with tool_calls, an assistant message with tool_calls must be followed by tool messages, unknown or missing roles, empty arrays, invalid content shapes, and on the Anthropic side broken user and assistant alternation, misplaced system, and unmatched tool_use and tool_result ids. Semantic problems - a wrong model name, exceeded context length, malformed function arguments - are different failures and stay out of scope.
Types catch shape errors at compile time, but the rules that produce most 400s are ordering rules across messages: whether this tool_call_id was declared two messages ago, whether every declared call got answered before the next user turn. Those only exist at runtime, after your history-trimming and retry logic has assembled the actual array. Paste that assembled array here and the cross-message pairing is checked directly - in any language, including logs from production.
Yes - parsing and diagnosis run entirely in your browser. The content never leaves the page: no upload, no storage, no analytics on the pasted text. And since the diagnosis only reads roles, ids and shapes, you can also redact content values before pasting if you prefer extra caution.