🚀 New: chi (χ) — an open-source autoresearch harness for fleets of LLM coding agents. Read the announcement.

Claude Opus 5.5 Now Rejects Its Own Thinking If You Edited the Prompt Under It

Claude Opus 5.5 and Fable 5.1 now bind every thinking block to the exact system prompt, tool list, and message history that produced it. Replay that block after editing any of the three and the API returns a 400, not a silent success, on accounts created after August 31, 2026. The fix, and the same day's inline-tools beta, both point the same direction: stop editing conversation history, only append to it.

Contents

Claude Opus 5.5 launched on September 22 with the usual round of pricing and context-window numbers, but the change worth an agent builder’s attention is smaller and easier to miss: a thinking block now carries a signature of the exact conversation prefix that produced it, and on newly created accounts, replaying that block after the prefix changed no longer silently works. It returns a 400.[1] The same day, a separate beta shipped for defining tools inside a mid-conversation message instead of editing the tools array.[2] Read together, they say the same thing about how Anthropic wants agent loops built from now on: never edit history, only append to it.

What a thinking block is bound to

Every response from Claude Opus 5.5 or Claude Fable 5.1 that includes a thinking block records enough state to check, on a later request, whether the top-level system field, the tools array, and every message before that block are byte-identical to what they were when the block was produced. Anthropic calls this the prefix check, and it validates exactly three things: the system prompt, the tool list, and the message history up to that point. Everything else in the request, effort, max_tokens, tool_choice, metadata, is not checked and doesn’t invalidate anything.[3]

If any of the three changed and you resend the block, the default behavior is to reject the request:

messages.1.content.0: Invalid `signature` in `thinking` block. The block
is bound to a different conversation. Remove the block, or set
`thinking.block_binding.prefix_mismatch_behavior` to "drop_block".

That enforcement is on by default only for accounts created on or after August 31, 2026, 00:00 UTC. Older accounts get the check silently: a mismatch is flagged in the response’s input_transformations array (behind the thinking-binding-controls-2026-08-01 beta header) but the request still succeeds, until you explicitly opt into the strict behavior yourself.[3] So this is not yet a universal breaking change, it’s a default that’s already live for anyone starting fresh, and an opt-in landmine for everyone else.

There’s a second, separate check that people conflate with this one: model binding, which asks whether the current model can even read a thinking block produced by a different model. That check is always on, never configurable, and just drops the block rather than erroring. Prefix binding is new and different: it’s not about which model wrote the reasoning, it’s about whether the conversation the reasoning happened in still exists in the form the model saw it.[3]

Why this lands on the same day as inline tool definitions

The prefix check makes explicit something that was already true operationally: the tools array and the system field sit at the very start of the hashed prefix Anthropic uses for prompt caching, so any edit to either one invalidates the cache for the entire conversation, not just the turn where the edit happened.[2] That’s been the quiet cost of dynamic tool discovery since prompt caching launched. An agent that queries an MCP server, gets back three tools it didn’t know about, and appends them to tools has been eating a full cache miss on every one of those turns, on every model, regardless of whether thinking was involved.

What changed on September 22 is that Anthropic gave that problem a first-class fix instead of leaving it as a workaround: the inline-tools-2026-09-15 beta lets you send a tool_addition block inside a mid-conversation system-role message, carrying a full tool definition, and the API treats it as new input appended after the cached prefix rather than an edit to the prefix itself. The tools array you sent at the start of the conversation never changes; the model just gets told, from this point in the transcript onward, that another tool exists.[2]

messages=[
    {"role": "user", "content": "How many orders shipped yesterday?"},
    {
        "role": "system",
        "content": [{
            "type": "tool_addition",
            "tool": {
                "type": "tool_definition",
                "definition": {
                    "name": "db_query",
                    "description": "Run a read-only SQL query against the analytics database.",
                    "input_schema": {
                        "type": "object",
                        "properties": {"sql": {"type": "string"}},
                        "required": ["sql"],
                    },
                },
            },
        }],
    },
]

A matching tool_removal block withdraws a tool the same way, by reference, without touching the array either. Once a tool has been defined this way, later turns can re-offer or retract it with a lightweight tool_reference block instead of resending the full schema.[2]

The actual consequence for agent loops

Put the two features next to each other and the design intent is obvious: Anthropic is telling agent builders that a conversation’s system and tools are not state you mutate, they’re state you declare once and then layer changes on top of, append-only. The prefix-binding check is what makes that intent enforceable instead of just a performance tip. Before September 22, editing tools mid-loop only cost you a cache miss, an invisible tax that showed up as a latency and spend number nobody investigated. Now, on new accounts with thinking on, it can cost you a hard failure the next time the model tries to replay reasoning it did before the edit.

The fix is the same in both cases: stop editing tools and system directly. Declare the full set of tools you might ever offer up front in tools (even ones you gate behind tool_removal early in the conversation), and use tool_addition/tool_removal system messages for anything that becomes known or changes shape later. Anthropic’s own guidance is blunt about it: “never edit earlier messages, append new system messages instead, to preserve cache and thinking block signatures."[2]

There’s still an escape hatch if you can’t restructure the loop immediately. Setting thinking.block_binding.prefix_mismatch_behavior to "drop_block" (behind the thinking-binding-controls-2026-08-01 header) turns the 400 back into a silent drop: the mismatched blocks and everything after them get discarded, unbilled, and the model just re-derives whatever reasoning it lost. That keeps the loop running, but it’s explicitly a stopgap. Anthropic’s own docs warn it “doesn’t fix the underlying edit” and “can cause increased token usage as the model re-creates dropped thinking,” which is another way of saying you’re now paying twice for reasoning you already paid for once, plus a cache miss on top.[3]

If you have an agent framework, in-house or third-party, that discovers MCP tools live and appends them to a running conversation, check whether it’s currently mutating tools directly. That pattern didn’t fail before. Depending on when the account was created and whether thinking is on, it now either fails outright or silently gets more expensive, and the same-day inline-tools beta is the sanctioned way out of both.

Sources

[1] https://platform.claude.com/docs/en/models/opus-5-5/whats-new-opus-5-5: Claude Opus 5.5 launch notes, “Thinking blocks are tied to the model and the conversation”

[2] https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages: cache-invalidation mechanics, tool_addition/tool_removal block formats, and the append-only guidance

[3] https://platform.claude.com/docs/en/build-with-claude/preserved-thinking: prefix-binding check definition, error text, account enforcement cutoff, and prefix_mismatch_behavior options