preserved-thinking changedbuild-with-claude/preserved-thinking
Nearest release: v2.1.312, published under an hour before this site recorded the change. Shown because the two are within 24 hours of each other. Nothing here says the release caused the edit.
Recorded here
Lines+79added
Lines−79removed
From line
4
where the diff opens
First seen
1 Sep 2026
this site's first read of the page
Recorded edits35to this page, all time
#### Patterns that don’t work with preserved thinking #### Patterns that don't work with preserved thinking
The whole hunk
from line 4, old and new numbered
/
from line 4
44description: Preserved thinking lets a model use a thinking block from an earlier turn only if that model or one of a fixed set of other models produced it and nothing before the block has changed.
55---
66
7Preserved thinking is a property of newer Claude models that guards against distillation. It determines whether the model can use a thinking block that you send back from an earlier turn. Starting with Claude Fable 5.1, when a `thinking` or `redacted_thinking` block comes back in a request, the API checks the block's `signature` for two things:
7Preserved thinking is a property of newer Claude models that guards against distillation. It determines whether the model can use a thinking block that you send back from an earlier turn. Starting with Claude Fable 5.1, when a `thinking` or `redacted_thinking` block comes back in a request, the API checks the block’s `signature` for two things:
88
9* **The model can read the block.** Each model reads its own thinking blocks and those of a fixed set of other models. Claude Fable 5.1 reads blocks from Claude Opus 5 and, on the Claude API, from Claude Opus 5.5; neither Claude Opus 5 nor Claude Opus 5.5 reads blocks from Claude Fable 5.1. If the current model can't read a block, the API drops it from that request without an error. See [Switching models mid-conversation](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#switching-models).
9* **The model can read the block.** Each model reads its own thinking blocks and those of a fixed set of other models. Claude Fable 5.1 reads blocks from Claude Opus 5 and, on the Claude API, from Claude Opus 5.5; neither Claude Opus 5 nor Claude Opus 5.5 reads blocks from Claude Fable 5.1. If the current model can’t read a block, the API drops it from that request without an error. See [Switching models mid-conversation](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#switching-models).
1010* **Nothing before the thinking block has changed.** The top-level `system` prompt, `tools`, and `messages` before the block are its prefix. If the prefix differs from what you sent when the block was produced, that block and every later thinking block are invalid, and the API rejects the request with a 400 error or drops the invalid blocks, whichever you choose. See [Keeping the prefix unchanged](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#prefix-check).
1111
1212Thinking blocks from Claude Sonnet 5.5 and Claude Haiku 5.5 are also tied to the account that produced them. See [Thinking blocks stay with the account that produced them](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#account-bound-thinking) for where the API enforces this.
1313
14The model check applies to every account. The API enforces the prefix check by default for accounts created on or after August 31, 2026, 00:00 UTC. On older accounts, it enforces the prefix check only on requests that set `thinking.block_binding.prefix_mismatch_behavior`. **Make your integration append-only regardless of your account's age**, so the same code works on every account, including newer accounts enforced by default.
14The model check applies to every account. The API enforces the prefix check by default for accounts created on or after August 31, 2026, 00:00 UTC. On older accounts, it enforces the prefix check only on requests that set `thinking.block_binding.prefix_mismatch_behavior`. **Make your integration append-only regardless of your account’s age**, so the same code works on every account, including newer accounts enforced by default.
1515
1616## Who needs to change anything
1717
18Nothing changes for you if Claude Code, claude.ai, Claude Managed Agents, or the Claude Agent SDK builds your requests, or if your code keeps `system` and `tools` fixed for a session and only ever appends to `messages`. Claude Mythos 5.1 and models before Claude Fable 5.1 don't run the prefix check. If you never send thinking blocks back, the prefix check has nothing to reject, and the model gets none of its earlier reasoning.
18Nothing changes for you if Claude Code, claude.ai, Claude Managed Agents, or the Claude Agent SDK builds your requests, or if your code keeps `system` and `tools` fixed for a session and only ever appends to `messages`. Claude Mythos 5.1 and models before Claude Fable 5.1 don’t run the prefix check. If you never send thinking blocks back, the prefix check has nothing to reject, and the model gets none of its earlier reasoning.
1919
2020Check your integration if, between two requests in one conversation, it does any of the following. Each item links to what to do instead:
2121
from line 28
2828* [Drops some `thinking` blocks and keeps later ones](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#append-assistant-turns-exactly-as-returned), or removes them and [later restores those blocks](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#prefix-check)
2929* [Rebuilds a saved session from templates](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#faq) instead of replaying what it sent
3030
31On an older account, none of these produces an error unless the request sets `prefix_mismatch_behavior`, so a run with no errors on your own key doesn't show whether your code is affected. If people run your tool with their own API keys, those on newer accounts get the 400 error before you do. To see what they see without changing how your requests behave, send the `thinking-binding-controls-2026-08-01` beta header. On an older account, each response then flags blocks that fail the check, and the model still reads them (see [Set the mismatch behavior and read `input_transformations`](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#preserved-thinking-controls)).
31On an older account, none of these produces an error unless the request sets `prefix_mismatch_behavior`, so a run with no errors on your own key doesn’t show whether your code is affected. If people run your tool with their own API keys, those on newer accounts get the 400 error before you do. To see what they see without changing how your requests behave, send the `thinking-binding-controls-2026-08-01` beta header. On an older account, each response then flags blocks that fail the check, and the model still reads them (see [Set the mismatch behavior and read `input_transformations`](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#preserved-thinking-controls)).
3232
3333Also check your integration if it sends Claude Sonnet 5.5 or Claude Haiku 5.5 thinking blocks from one account in a request made by a different account. See [Thinking blocks stay with the account that produced them](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#account-bound-thinking).
3434
from line 36
3636
3737Claude Fable 5.1 and Claude Mythos 5.1 read thinking blocks produced by each other and by earlier Claude models. No other model reads thinking blocks from Claude Fable 5.1 or Claude Mythos 5.1.
3838
39Claude Opus 5.5 reads thinking blocks from Claude Opus 5, from earlier Opus, Sonnet, and Haiku models, and, on the Claude API and Claude Models on Google Cloud (Google managed), from Claude Sonnet 5.5 and Claude Haiku 5.5, but not from Claude Fable or Claude Mythos models. On the Claude API, Claude Fable 5.1 and Claude Mythos 5.1 read thinking blocks from Claude Opus 5.5; no other model does. So a conversation that moves from Claude Opus 5 onto Claude Opus 5.5 keeps its reasoning, and so does one that moves from Claude Opus 5.5 up to Claude Fable 5.1 or Claude Mythos 5.1 on the Claude API. One that moves from Claude Fable 5.1 or Claude Mythos 5.1 to Claude Opus 5.5, or from Claude Opus 5.5 to any model other than those two, runs the turns after the switch without the previous model's reasoning. The blocks are dropped, not rejected, as described later in this section.
39Claude Opus 5.5 reads thinking blocks from Claude Opus 5, from earlier Opus, Sonnet, and Haiku models, and, on the Claude API and Claude Models on Google Cloud (Google managed), from Claude Sonnet 5.5 and Claude Haiku 5.5, but not from Claude Fable or Claude Mythos models. On the Claude API, Claude Fable 5.1 and Claude Mythos 5.1 read thinking blocks from Claude Opus 5.5; no other model does. So a conversation that moves from Claude Opus 5 onto Claude Opus 5.5 keeps its reasoning, and so does one that moves from Claude Opus 5.5 up to Claude Fable 5.1 or Claude Mythos 5.1 on the Claude API. One that moves from Claude Fable 5.1 or Claude Mythos 5.1 to Claude Opus 5.5, or from Claude Opus 5.5 to any model other than those two, runs the turns after the switch without the previous model’s reasoning. The blocks are dropped, not rejected, as described later in this section.
4040
41Claude Sonnet 5.5 reads thinking blocks from Claude Sonnet 5, Claude Opus 4.8, Claude Haiku 4.5, and earlier models, and, on the Claude API and Claude Models on Google Cloud (Google managed), from Claude Haiku 5.5, but not from Claude Opus 5, Claude Opus 5.5, or any Claude Fable or Claude Mythos model. On those two platforms, Claude Opus 5.5 reads thinking blocks from Claude Sonnet 5.5; no other model does. So a conversation that moves from Claude Sonnet 5 onto Claude Sonnet 5.5 keeps its reasoning, and so does one that moves from Claude Sonnet 5.5 up to Claude Opus 5.5 on those platforms. One that moves onto Claude Sonnet 5.5 from Claude Opus 5, Claude Opus 5.5, or a Claude Fable or Claude Mythos model runs the turns after the switch without the previous model's reasoning. So does any other move away from Claude Sonnet 5.5, for example a [server-side fallback](https://platform.claude.com/docs/en/build-with-claude/refusals-and-fallback#server-side-fallback) to Claude Sonnet 5.
41Claude Sonnet 5.5 reads thinking blocks from Claude Sonnet 5, Claude Opus 4.8, Claude Haiku 4.5, and earlier models, and, on the Claude API and Claude Models on Google Cloud (Google managed), from Claude Haiku 5.5, but not from Claude Opus 5, Claude Opus 5.5, or any Claude Fable or Claude Mythos model. On those two platforms, Claude Opus 5.5 reads thinking blocks from Claude Sonnet 5.5; no other model does. So a conversation that moves from Claude Sonnet 5 onto Claude Sonnet 5.5 keeps its reasoning, and so does one that moves from Claude Sonnet 5.5 up to Claude Opus 5.5 on those platforms. One that moves onto Claude Sonnet 5.5 from Claude Opus 5, Claude Opus 5.5, or a Claude Fable or Claude Mythos model runs the turns after the switch without the previous model’s reasoning. So does any other move away from Claude Sonnet 5.5, for example a [server-side fallback](https://platform.claude.com/docs/en/build-with-claude/refusals-and-fallback#server-side-fallback) to Claude Sonnet 5.
4242
43Claude Haiku 5.5 reads thinking blocks from Claude Sonnet 5, Claude Opus 4.8, Claude Haiku 4.5, and earlier models, but not from Claude Opus 5, Claude Opus 5.5, Claude Sonnet 5.5, or any Claude Fable or Claude Mythos model. On the Claude API and Claude Models on Google Cloud (Google managed), Claude Opus 5.5 and Claude Sonnet 5.5 read thinking blocks from Claude Haiku 5.5; no other model does. So a conversation that moves from Claude Haiku 4.5 onto Claude Haiku 5.5 keeps its reasoning, and so does one that moves from Claude Haiku 5.5 up to Claude Opus 5.5 or Claude Sonnet 5.5 on the Claude API and Claude Models on Google Cloud (Google managed). One that moves onto Claude Haiku 5.5 from Claude Opus 5, Claude Opus 5.5, Claude Sonnet 5.5, or a Claude Fable or Claude Mythos model runs the turns after the switch without the previous model's reasoning, and so does any other move away from Claude Haiku 5.5.
43Claude Haiku 5.5 reads thinking blocks from Claude Sonnet 5, Claude Opus 4.8, Claude Haiku 4.5, and earlier models, but not from Claude Opus 5, Claude Opus 5.5, Claude Sonnet 5.5, or any Claude Fable or Claude Mythos model. On the Claude API and Claude Models on Google Cloud (Google managed), Claude Opus 5.5 and Claude Sonnet 5.5 read thinking blocks from Claude Haiku 5.5; no other model does. So a conversation that moves from Claude Haiku 4.5 onto Claude Haiku 5.5 keeps its reasoning, and so does one that moves from Claude Haiku 5.5 up to Claude Opus 5.5 or Claude Sonnet 5.5 on the Claude API and Claude Models on Google Cloud (Google managed). One that moves onto Claude Haiku 5.5 from Claude Opus 5, Claude Opus 5.5, Claude Sonnet 5.5, or a Claude Fable or Claude Mythos model runs the turns after the switch without the previous model’s reasoning, and so does any other move away from Claude Haiku 5.5.
4444
45* **A conversation that moves to Claude Fable 5.1 from an earlier model, or from Claude Opus 5.5 on the Claude API, keeps its reasoning.** The earlier model's thinking blocks stay readable, so the model thinks as usual from the first turn after the switch.
46* **A conversation that moves from Claude Fable 5.1 to any model other than Claude Mythos 5.1 loses Claude Fable 5.1's reasoning for that request.** This occurs when a router sends a turn to a cheaper model, after a [classifier refusal fallback](https://platform.claude.com/docs/en/build-with-claude/refusals-and-fallback), or during a [server-side fallback](https://platform.claude.com/docs/en/build-with-claude/refusals-and-fallback#server-side-fallback). The API removes the unreadable blocks before the prompt reaches the model. They aren't billed and don't count toward `input_tokens`.
45* **A conversation that moves to Claude Fable 5.1 from an earlier model, or from Claude Opus 5.5 on the Claude API, keeps its reasoning.** The earlier model’s thinking blocks stay readable, so the model thinks as usual from the first turn after the switch.
46* **A conversation that moves from Claude Fable 5.1 to any model other than Claude Mythos 5.1 loses Claude Fable 5.1’s reasoning for that request.** This occurs when a router sends a turn to a cheaper model, after a [classifier refusal fallback](https://platform.claude.com/docs/en/build-with-claude/refusals-and-fallback), or during a [server-side fallback](https://platform.claude.com/docs/en/build-with-claude/refusals-and-fallback#server-side-fallback). The API removes the unreadable blocks before the prompt reaches the model. They aren’t billed and don’t count toward `input_tokens`.
4747
48Keep sending the full history on every request, thinking blocks included, and let the API drop what the current model can't read. The API never edits your `messages` array, so the dropped blocks stay in your history. When the same history goes back to Claude Fable 5.1, its blocks are readable again, along with the earlier model's thinking. The reasoning is permanently lost only if your client removes the blocks itself, for example a harness that strips thinking on a model switch or rebuilds the history from what each model used.
48Keep sending the full history on every request, thinking blocks included, and let the API drop what the current model can’t read. The API never edits your `messages` array, so the dropped blocks stay in your history. When the same history goes back to Claude Fable 5.1, its blocks are readable again, along with the earlier model’s thinking. The reasoning is permanently lost only if your client removes the blocks itself, for example a harness that strips thinking on a model switch or rebuilds the history from what each model used.
4949
5050<Frame>
5151 
from line 65
6565}
6666```
6767
68Without the header, the drop is silent. This entry isn't a bug in your integration, and `prefix_mismatch_behavior` has no effect on it: a block the current model can't read is always dropped.
68Without the header, the drop is silent. This entry isn’t a bug in your integration, and `prefix_mismatch_behavior` has no effect on it: a block the current model can’t read is always dropped.
6969
7070## Thinking blocks stay with the account that produced them
7171
72Thinking blocks that Claude Sonnet 5.5 or Claude Haiku 5.5 produces work only in the account that produced them, or in an account linked to it. When another account sends one of these blocks, the API drops the block before the model sees it, and the request succeeds. The model answers without the reasoning in the dropped blocks. Blocks from earlier models aren't affected.
72Thinking blocks that Claude Sonnet 5.5 or Claude Haiku 5.5 produces work only in the account that produced them, or in an account linked to it. When another account sends one of these blocks, the API drops the block before the model sees it, and the request succeeds. The model answers without the reasoning in the dropped blocks. Blocks from earlier models aren’t affected.
7373
7474On the Claude API and Claude Models on Google Cloud (Google managed), with the `thinking-binding-controls-2026-08-01` [beta header](https://platform.claude.com/docs/en/api/beta-headers), the response lists each dropped block in `input_transformations` as a `thinking_dropped` entry with `reason: "organization_binding_mismatch"`. Without the header, the drop is silent.
7575
76The API can also leave out a thinking block that another account produced, if that account is no longer in good standing. This applies to requests to Claude Fable and Claude Mythos models, Claude Opus 5, Claude Opus 5.5, Claude Sonnet 5.5, and Claude Haiku 5.5. The request still succeeds, and the model answers without that block's reasoning.
76The API can also leave out a thinking block that another account produced, if that account is no longer in good standing. This applies to requests to Claude Fable and Claude Mythos models, Claude Opus 5, Claude Opus 5.5, Claude Sonnet 5.5, and Claude Haiku 5.5. The request still succeeds, and the model answers without that block’s reasoning.
7777
7878## Keeping the prefix unchanged
7979
from line 85
8585
8686Note: With server-side [compaction](https://platform.claude.com/docs/en/build-with-claude/compaction), the checked prefix starts at the most recent compaction block.
8787
88Request parameters outside those three fields, such as `effort`, `max_tokens`, `output_config`, `tool_choice`, and `metadata`, aren't part of the prefix check, and neither are `cache_control` markers. [What counts as an edit](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#what-counts-as-an-edit) has the full list.
88Request parameters outside those three fields, such as `effort`, `max_tokens`, `output_config`, `tool_choice`, and `metadata`, aren’t part of the prefix check, and neither are `cache_control` markers. [What counts as an edit](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#what-counts-as-an-edit) has the full list.
8989
90Earlier thinking blocks aren't in the prefix, but each thinking block records which thinking block came before it, across turns. You can remove thinking blocks from the start of the history (oldest first), from the end, or all of them. What fails is a gap: the thinking blocks you keep must be an unbroken run of the original sequence, so removing one from the middle invalidates the thinking blocks after it. Once you remove a block, leave it out. Putting it back invalidates the thinking blocks produced while it was gone.
90Earlier thinking blocks aren’t in the prefix, but each thinking block records which thinking block came before it, across turns. You can remove thinking blocks from the start of the history (oldest first), from the end, or all of them. What fails is a gap: the thinking blocks you keep must be an unbroken run of the original sequence, so removing one from the middle invalidates the thinking blocks after it. Once you remove a block, leave it out. Putting it back invalidates the thinking blocks produced while it was gone.
9191
9292Keep `system` and `tools` fixed for the session and treat `messages` as append-only. The same discipline keeps the prefix stable for [prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching): most edits that invalidate thinking also restart the cache.
9393
from line 96
9696You choose with `thinking.block_binding.prefix_mismatch_behavior`:
9797
9898* **`"error"` (the default):** the API rejects the request with a 400 `invalid_request_error` that names the first failing block.
99* **`"drop_block"`:** the API drops each failing block and every thinking block after it, and the request succeeds. Dropped blocks aren't billed. The model answers that turn without using reasoning from dropped blocks, and the prompt cache restarts at the edit. The response lists each dropped block in `input_transformations` (on the `message_start` event when streaming) with `reason: "prefix_binding_mismatch"`.
99* **`"drop_block"`:** the API drops each failing block and every thinking block after it, and the request succeeds. Dropped blocks aren’t billed. The model answers that turn without using reasoning from dropped blocks, and the prompt cache restarts at the edit. The response lists each dropped block in `input_transformations` (on the `message_start` event when streaming) with `reason: "prefix_binding_mismatch"`.
100100
101101<Warning>
102 `"drop_block"` keeps requests succeeding, but it doesn't fix the edit that caused the mismatch, and it has a cost. Dropping blocks can significantly increase the output tokens generated, especially when blocks are dropped on every turn. For more information, see [Output-token cost of `"drop_block"`](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#drop-block-cost). Dropped blocks aren't billed, but Claude re-creates the reasoning it can no longer read, so it generates more output tokens. Output quality might also change on tasks where later turns build on earlier reasoning. Use `"drop_block"` as a stopgap while you fix the edit, but not as a long-term setting.
102 `"drop_block"` keeps requests succeeding, but it doesn’t fix the edit that caused the mismatch, and it has a cost. Dropping blocks can significantly increase the output tokens generated, especially when blocks are dropped on every turn. For more information, see [Output-token cost of `"drop_block"`](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#drop-block-cost). Dropped blocks aren’t billed, but Claude re-creates the reasoning it can no longer read, so it generates more output tokens. Output quality might also change on tasks where later turns build on earlier reasoning. Use `"drop_block"` as a stopgap while you fix the edit, but not as a long-term setting.
103103</Warning>
104104
105Count the responses in each session whose `input_transformations` has a `prefix_binding_mismatch` entry, alert on them, and replace each edit with the matching pattern in [Make changes without editing the prefix](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#replace-prefix-edits). In the Message Batches API, an item that leaves the field unset doesn't fail. Where the API enforces the check by default, it drops the failing blocks instead. Set `"error"` explicitly there if you want batch items to fail.
105Count the responses in each session whose `input_transformations` has a `prefix_binding_mismatch` entry, alert on them, and replace each edit with the matching pattern in [Make changes without editing the prefix](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#replace-prefix-edits). In the Message Batches API, an item that leaves the field unset doesn’t fail. Where the API enforces the check by default, it drops the failing blocks instead. Set `"error"` explicitly there if you want batch items to fail.
106106
107107Both the field and the `input_transformations` array require the `thinking-binding-controls-2026-08-01` [beta header](https://platform.claude.com/docs/en/api/beta-headers). [Set the mismatch behavior and read `input_transformations`](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#preserved-thinking-controls) shows the request in each SDK.
108108
from line 112
112112messages.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".
113113```
114114
115If the request didn't send the beta header, the message continues:
115If the request didn’t send the beta header, the message continues:
116116
117117```text wrap
118118That setting requires the `thinking-binding-controls-2026-08-01` value in the `anthropic-beta` header.
from line 120
120120
121121It usually ends with a sentence naming what changed, for example that the `system` prompt or the `tools` list differs from when the block was created. For what that sentence can name, see [A 400 error says a thinking block signature is invalid](https://platform.claude.com/docs/en/build-with-claude/thinking-troubleshooting#error-thinking-block-signature) in Troubleshooting thinking.
122122
123A tampered or undecryptable signature is a different failure. It always returns a 400 (``Invalid `signature` in `thinking` block`` with no sentence about the conversation), and `prefix_mismatch_behavior` doesn't apply to it.
123A tampered or undecryptable signature is a different failure. It always returns a 400 (``Invalid `signature` in `thinking` block`` with no sentence about the conversation), and `prefix_mismatch_behavior` doesn’t apply to it.
124124
125125#### Output-token cost of `"drop_block"`
126126
127127When Claude can no longer read reasoning from earlier turns, it re-creates that reasoning, so requests that drop thinking blocks generate more output tokens and can take longer to respond. A drop on every turn costs more than an occasional drop, and how much more depends on your harness: how often it edits the prefix and how much each turn builds on earlier reasoning.
128128
129Before you rely on `"drop_block"`, run evals on your own workload to measure the increase. If it's small, `"drop_block"` can keep requests succeeding while you fix the edit. Either way, fixing the edit with a pattern from [Make changes without editing the prefix](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#replace-prefix-edits) is a better long-term solution.
129Before you rely on `"drop_block"`, run evals on your own workload to measure the increase. If it’s small, `"drop_block"` can keep requests succeeding while you fix the edit. Either way, fixing the edit with a pattern from [Make changes without editing the prefix](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#replace-prefix-edits) is a better long-term solution.
130130
131131#### Handle the error in code
132132
133This is the 400 `invalid_request_error` shown earlier in this section. Don't resend the same body: it fails the same way every time. Retry once with the beta header and `prefix_mismatch_behavior: "drop_block"`, as a stopgap ([`"drop_block"` can raise output tokens](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#drop-block-cost)), and store that choice with the session so every later request sends it too, including after a restart. On Claude Sonnet 5.5 and Claude Haiku 5.5, `block_binding` works only with `thinking: {"type": "adaptive"}`. With `between_tools` on Claude Sonnet 5.5, or `thinking: {"type": "disabled"}` on Claude Haiku 5.5, keep the history append-only, or strip the thinking blocks from the edited turn on. If you can't send the beta header, remove every `thinking` and `redacted_thinking` block from the history once, leave them out, and continue. Then fix the edit that caused the mismatch.
133This is the 400 `invalid_request_error` shown earlier in this section. Don’t resend the same body: it fails the same way every time. Retry once with the beta header and `prefix_mismatch_behavior: "drop_block"`, as a stopgap ([`"drop_block"` can raise output tokens](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#drop-block-cost)), and store that choice with the session so every later request sends it too, including after a restart. On Claude Sonnet 5.5 and Claude Haiku 5.5, `block_binding` works only with `thinking: {"type": "adaptive"}`. With `between_tools` on Claude Sonnet 5.5, or `thinking: {"type": "disabled"}` on Claude Haiku 5.5, keep the history append-only, or strip the thinking blocks from the edited turn on. If you can’t send the beta header, remove every `thinking` and `redacted_thinking` block from the history once, leave them out, and continue. Then fix the edit that caused the mismatch.
134134
135135### Set the mismatch behavior and read `input_transformations`
136136
from line 139
139139* A top-level `input_transformations` array on every response
140140* A `block_binding` object on the `thinking` configuration, whose one field is `prefix_mismatch_behavior`
141141
142`block_binding` is accepted alongside `thinking.type: "adaptive"` and `thinking.type: "enabled"`. On Claude Sonnet 5.5 and Claude Haiku 5.5, `block_binding` works only with `thinking: {"type": "adaptive"}`. Sending it with `between_tools` on Claude Sonnet 5.5, or with `thinking: {"type": "disabled"}` on Claude Haiku 5.5, returns a 400 error. Sending it without the beta header returns a 400 error whose message ends `block_binding: Extra inputs are not permitted`. Models that don't run the prefix check accept the object and report only model-check drops, so one request body works across models. The API reference calls the prefix check the conversation check.
142`block_binding` is accepted alongside `thinking.type: "adaptive"` and `thinking.type: "enabled"`. On Claude Sonnet 5.5 and Claude Haiku 5.5, `block_binding` works only with `thinking: {"type": "adaptive"}`. Sending it with `between_tools` on Claude Sonnet 5.5, or with `thinking: {"type": "disabled"}` on Claude Haiku 5.5, returns a 400 error. Sending it without the beta header returns a 400 error whose message ends `block_binding: Extra inputs are not permitted`. Models that don’t run the prefix check accept the object and report only model-check drops, so one request body works across models. The API reference calls the prefix check the conversation check.
143143
144The following request opts into dropping rather than rejecting. On a first turn there's nothing to replay, so `input_transformations` comes back empty:
144The following request opts into dropping rather than rejecting. On a first turn there’s nothing to replay, so `input_transformations` comes back empty:
145145
146146<CodeGroup>
147147 ```bash cURL
from line 397
397397
398398Under the beta header, every response from a thinking-capable model carries `input_transformations`. Each entry names one thinking block by its `path` (for example `messages.1.content.0`) and gives a `reason`. There are two entry types:
399399
400* **`thinking_dropped`:** the API drops the block before the model reads it, and the block isn't billed. The `reason` is `prefix_binding_mismatch` or `model_binding_mismatch` (see [Switching models mid-conversation](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#switching-models)). The `reason` can also be `organization_binding_mismatch`, which means the block came from another account (see [Thinking blocks stay with the account that produced them](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#account-bound-thinking)).
401* **`thinking_mismatch_allowed`:** the block fails the prefix check, but the API doesn't enforce that check for this request, so the block reaches the model unchanged and is billed. The `reason` is always `prefix_binding_mismatch`. This entry appears only on requests where the API doesn't enforce the check by default, such as those from an older account (see [When the API enforces the check](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#enforcement)). Setting `prefix_mismatch_behavior` to either value opts the request into enforcement, so a request that sets it never gets this entry.
400* **`thinking_dropped`:** the API drops the block before the model reads it, and the block isn’t billed. The `reason` is `prefix_binding_mismatch` or `model_binding_mismatch` (see [Switching models mid-conversation](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#switching-models)). The `reason` can also be `organization_binding_mismatch`, which means the block came from another account (see [Thinking blocks stay with the account that produced them](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#account-bound-thinking)).
401* **`thinking_mismatch_allowed`:** the block fails the prefix check, but the API doesn’t enforce that check for this request, so the block reaches the model unchanged and is billed. The `reason` is always `prefix_binding_mismatch`. This entry appears only on requests where the API doesn’t enforce the check by default, such as those from an older account (see [When the API enforces the check](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#enforcement)). Setting `prefix_mismatch_behavior` to either value opts the request into enforcement, so a request that sets it never gets this entry.
402402
403The array is empty when no block was dropped and none failed the prefix check. Ignore entries whose `type` or `reason` you don't recognize, because later checks add values.
403The array is empty when no block was dropped and none failed the prefix check. Ignore entries whose `type` or `reason` you don’t recognize, because later checks add values.
404404
405When [streaming](https://platform.claude.com/docs/en/build-with-claude/streaming), the array arrives on the `message` object in the `message_start` event. After a mid-stream server-side fallback, the final `message_delta` event carries it again with the serving model's entries. In a [message batch](https://platform.claude.com/docs/en/build-with-claude/batch-processing), an item whose block fails the prefix check under an explicit `"error"` resolves as `errored`. An item that leaves the field unset doesn't fail. Where the API enforces the check by default, it drops the failing blocks instead. The [token counting](https://platform.claude.com/docs/en/build-with-claude/token-counting) endpoint runs the same prefix check and returns the same 400.
405When [streaming](https://platform.claude.com/docs/en/build-with-claude/streaming), the array arrives on the `message` object in the `message_start` event. After a mid-stream server-side fallback, the final `message_delta` event carries it again with the serving model’s entries. In a [message batch](https://platform.claude.com/docs/en/build-with-claude/batch-processing), an item whose block fails the prefix check under an explicit `"error"` resolves as `errored`. An item that leaves the field unset doesn’t fail. Where the API enforces the check by default, it drops the failing blocks instead. The [token counting](https://platform.claude.com/docs/en/build-with-claude/token-counting) endpoint runs the same prefix check and returns the same 400.
406406
407407### When the API enforces the check
408408
from line 411
411411* **Accounts created on or after August 31, 2026, 00:00 UTC:** the API checks Claude Fable 5.1, Claude Opus 5.5, Claude Sonnet 5.5, and Claude Haiku 5.5 requests and applies `"error"` unless you set `"drop_block"`. The same definition of a new account applies to the Claude API and to cloud platforms.
412412* **Older accounts:** the API enforces the check only on requests that set `prefix_mismatch_behavior`. Setting the field opts a request in, so you can see what a new account sees without creating one. On requests that leave it unset, the API still runs the check but lets failing blocks through to the model. With the beta header, the response lists each one in `input_transformations` as `thinking_mismatch_allowed`, so you can find prefix edits without changing what the model receives.
413413
414To find out which group your account is in, take a Claude Fable 5.1 conversation that contains a thinking block, change something before that block, and send it to Claude Fable 5.1 without the beta header or the `block_binding` field. A 400 response that names the header means your account is enforced by default. A 200 response means it isn't. To confirm, send the same request again with the beta header, still without `block_binding`: the response lists every thinking block after your edit in `input_transformations` as `thinking_mismatch_allowed`.
414To find out which group your account is in, take a Claude Fable 5.1 conversation that contains a thinking block, change something before that block, and send it to Claude Fable 5.1 without the beta header or the `block_binding` field. A 400 response that names the header means your account is enforced by default. A 200 response means it isn’t. To confirm, send the same request again with the beta header, still without `block_binding`: the response lists every thinking block after your edit in `input_transformations` as `thinking_mismatch_allowed`.
415415
416416### What counts as an edit
417417
from line 426
426426| Change `model` | Valid for blocks that the new model can read; the API drops the others without an error (see [Switching models mid-conversation](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#switching-models)) |
427427| Add, move, or remove `cache_control` markers | Valid |
428428| A rotating signed URL that returns the same bytes | Valid |
429| Server-side compaction or context editing removes or replaces content | Valid (the check compares what you sent, not the server's edited copy) |
429| Server-side compaction or context editing removes or replaces content | Valid (the check compares what you sent, not the server’s edited copy) |
430430| A cleared [turn-scoped system message](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#per-turn-reminders) left in place | Valid |
431431| Edit, reorder, or delete any earlier `user`, `assistant`, or `system` message | Invalid, except when the signed block from [on-demand compaction](https://platform.claude.com/docs/en/build-with-claude/compaction-on-demand) replaces the messages it summarizes, under the [conditions for kept thinking](https://platform.claude.com/docs/en/build-with-claude/compaction-thinking-blocks#conditions-for-kept-thinking-to-stay-valid) |
432432| Re-render the context you put in the first user message with a changed value | Invalid for every thinking block |
from line 726
726726
727727* **Empty on every turn of a session that contains `thinking` blocks:** your integration keeps the prefix intact.
728728* **`reason: "prefix_binding_mismatch"`:** something before the block at `path` changed since the previous request. Diff `system`, `tools`, and `messages` up to that turn to find it, or resend the request with `"error"`: the 400 usually ends with a sentence naming what changed. Then find the matching replacement in [Make changes without editing the prefix](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#replace-prefix-edits).
729* **`reason: "model_binding_mismatch"`:** the conversation moved to a model that can't read the earlier model's blocks. This isn't a prefix edit. See [Switching models mid-conversation](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#switching-models).
729* **`reason: "model_binding_mismatch"`:** the conversation moved to a model that can’t read the earlier model’s blocks. This isn’t a prefix edit. See [Switching models mid-conversation](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#switching-models).
730730* **`reason: "organization_binding_mismatch"`:** the block came from another account. See [Thinking blocks stay with the account that produced them](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#account-bound-thinking).
731731
732732To see a failure on purpose, send a third turn from the earlier example and add a `system` prompt to that request only, so that it differs from the first two requests, which had none. With `"drop_block"`, the dropped count is no longer 0: the response has one entry for each thinking block in the history, each with `reason: "prefix_binding_mismatch"`. With `"error"`, the request returns the 400 described in [What the API does with an invalid block](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#mismatch-behavior), and its last sentence names the `system` prompt. If the count is still 0, there was nothing to check: confirm that the model is claude-fable-5-1, that the request sets `block_binding`, that the history you sent contains `thinking` blocks, and that the first two requests had no `system` prompt.
from line 740
740740* A switch to another model and back
741741* A save, a restart, and a resume on a later date
742742
743On an older account, you can also watch production traffic without opting into enforcement: send the beta header, leave `block_binding` out, and log `input_transformations`. Sending the header alone doesn't change what the model receives. Every thinking block that comes after the content you edited fails the check and gets its own `thinking_mismatch_allowed` entry, with the same `path` and `reason` fields as a `thinking_dropped` entry. Blocks before the edit still pass. An edit to `system` or `tools` comes before every block, so it fails every thinking block in the request. One entry looks like this:
743On an older account, you can also watch production traffic without opting into enforcement: send the beta header, leave `block_binding` out, and log `input_transformations`. Sending the header alone doesn’t change what the model receives. Every thinking block that comes after the content you edited fails the check and gets its own `thinking_mismatch_allowed` entry, with the same `path` and `reason` fields as a `thinking_dropped` entry. Blocks before the edit still pass. An edit to `system` or `tools` comes before every block, so it fails every thinking block in the request. One entry looks like this:
744744
745745```json
746746{
from line 1036
10361036
10371037The third response flags every thinking block from the earlier turns, one for each turn in this run, because the new system prompt comes before all of them. The model still read them.
10381038
1039Handle these entries as you would `prefix_binding_mismatch` drops. The edit is before the first block listed: diff `system`, `tools`, and `messages` up to that block's `path` against the previous request to find it, then replace it with the matching pattern in [Make changes without editing the prefix](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#replace-prefix-edits). On a new account, or on any request that sets `prefix_mismatch_behavior`, the API instead rejects the request or drops the failing blocks. The entries are a lower bound on what enforcement would remove: with `"drop_block"`, a failing block also takes the rest of that turn's thinking blocks with it, and removing one block can make the next one fail too. When the API only records the check, it evaluates each block on its own and lists a block only if that block fails.
1039Handle these entries as you would `prefix_binding_mismatch` drops. The edit is before the first block listed: diff `system`, `tools`, and `messages` up to that block’s `path` against the previous request to find it, then replace it with the matching pattern in [Make changes without editing the prefix](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#replace-prefix-edits). On a new account, or on any request that sets `prefix_mismatch_behavior`, the API instead rejects the request or drops the failing blocks. The entries are a lower bound on what enforcement would remove: with `"drop_block"`, a failing block also takes the rest of that turn’s thinking blocks with it, and removing one block can make the next one fail too. When the API only records the check, it evaluates each block on its own and lists a block only if that block fails.
10401040
10411041## Make changes without editing the prefix
10421042
from line 1049
10491049| Clearing or shortening old `tool_result` content, or re-encoding old images, in place | Shorten a tool result or downscale an image before the first time you send it, not after. To clear old results later, [trim context on the server](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#server-side-trimming) with `clear_tool_uses_20250919` | `context-management-2025-06-27` |
10501050| Injecting a reminder and deleting it on the next request | A [turn-scoped system message](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#per-turn-reminders) (`clear_at: "next_user_message"`) | `mid-conversation-system-clear-at-2026-08-21` |
10511051| Adding or removing entries in `tools` | [`tool_addition` and `tool_removal` blocks](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#tool-changes) | `inline-tools-2026-09-15` (add `mcp-client-2026-09-15` when the tool comes from an MCP server connected through the MCP connector), or the older `mid-conversation-tool-changes-2026-07-01`, which works on the Claude API, Amazon Bedrock, and Claude Models on Google Cloud (Google managed) |
1052| Changing top-level `output_config.effort` (restarts the cache, doesn't affect thinking) | A [per-message `output_config`](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#effort-changes) | `mid-conversation-output-config-2026-07-01` |
1052| Changing top-level `output_config.effort` (restarts the cache, doesn’t affect thinking) | A [per-message `output_config`](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#effort-changes) | `mid-conversation-output-config-2026-07-01` |
10531053| Dropping or summarizing old turns on the client | [On-demand compaction](https://platform.claude.com/docs/en/build-with-claude/compaction-on-demand) to keep the recent turns with their thinking, other server-side [compaction or context editing](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#server-side-trimming), or [client-side compaction](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#custom-compaction-on-the-client) that keeps no stale thinking | `compact-2026-09-04` |
10541054| An image or document URL whose bytes change between requests | A [`file_id` from the Files API](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#files-by-id), or base64 | None |
10551055
1056All of these assume you [send assistant turns back exactly as returned](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#append-assistant-turns-exactly-as-returned). Mid-conversation system messages, turn-scoped system messages, and tool changes aren't available on every model: [Mid-conversation system messages and tool changes](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages) lists the models that accept them. If your code serves several models, keep editing the top-level `system` prompt for the models that don't accept them.
1056All of these assume you [send assistant turns back exactly as returned](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#append-assistant-turns-exactly-as-returned). Mid-conversation system messages, turn-scoped system messages, and tool changes aren’t available on every model: [Mid-conversation system messages and tool changes](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages) lists the models that accept them. If your code serves several models, keep editing the top-level `system` prompt for the models that don’t accept them.
10571057
10581058To use several betas in one request on the Claude API, combine their names in one `anthropic-beta` header:
10591059
from line 1103
11031103
11041104### Send per-turn reminders as turn-scoped system messages
11051105
1106A common prefix edit is the per-turn nudge: a line such as "request independent reads together" or "you haven't updated the user in a while" that your code appends after each batch of tool results. To keep reminders from piling up, send each nudge as a [mid-conversation system message](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#new-instructions) with `clear_at: "next_user_message"`, placed after the `tool_result` user message. `clear_at` requires the beta header `mid-conversation-system-clear-at-2026-08-21`. The following `messages` array is the request after two tool calls and their results. `messages[3]` is the previous request's nudge, left in place, and `messages[6]` is this request's copy:
1106A common prefix edit is the per-turn nudge: a line such as “request independent reads together” or “you haven’t updated the user in a while” that your code appends after each batch of tool results. To keep reminders from piling up, send each nudge as a [mid-conversation system message](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#new-instructions) with `clear_at: "next_user_message"`, placed after the `tool_result` user message. `clear_at` requires the beta header `mid-conversation-system-clear-at-2026-08-21`. The following `messages` array is the request after two tool calls and their results. `messages[3]` is the previous request’s nudge, left in place, and `messages[6]` is this request’s copy:
11071107
11081108```json
11091109[
from line 1153
11531153]
11541154```
11551155
1156A user message that contains only `tool_result` blocks counts as the "next user message," so `messages[3]` is already cleared. It adds nothing to what the model receives and costs no input tokens, but because it's still in the array, the thinking in `messages[4]` stays valid. `messages[6]` is the copy the model receives this turn. On later requests, keep both where they are and append a fresh copy after the next `tool_result` message.
1156A user message that contains only `tool_result` blocks counts as the “next user message,” so `messages[3]` is already cleared. It adds nothing to what the model receives and costs no input tokens, but because it’s still in the array, the thinking in `messages[4]` stays valid. `messages[6]` is the copy the model receives this turn. On later requests, keep both where they are and append a fresh copy after the next `tool_result` message.
11571157
11581158### Add or remove tools with `tool_addition` and `tool_removal`
11591159
from line 1161
11611161
11621162You have two ways to use these blocks:
11631163
1164* **Declare every tool up front.** Put every tool the session might need in `tools` on the first request, with `defer_loading: true` on any that you don't want to offer to the model yet. Then turn tools on and off with `tool_addition` and `tool_removal` blocks that name them.
1164* **Declare every tool up front.** Put every tool the session might need in `tools` on the first request, with `defer_loading: true` on any that you don’t want to offer to the model yet. Then turn tools on and off with `tool_addition` and `tool_removal` blocks that name them.
11651165* **Start with a snapshot and add tools as you go.** Put the tools you know about in `tools` on the first request. When you learn about another tool, define it inside a `tool_addition` block instead of editing `tools`.
11661166
11671167Either way, `tools` never changes, so earlier thinking stays valid and the prompt cache still hits, with the one exception noted later in this section.
from line 1190
11901190}
11911191```
11921192
1193Sometimes you can't declare a tool up front because you don't know its schema yet: a tool your application discovers at runtime, or an MCP server that connects after the first turn. Define it inside the `tool_addition` block instead of touching `tools`. With `inline-tools-2026-09-15`, the block's `tool` can be `{"type": "tool_definition", "definition": {...}}`, carrying the same entry you would have put in `tools`:
1193Sometimes you can’t declare a tool up front because you don’t know its schema yet: a tool your application discovers at runtime, or an MCP server that connects after the first turn. Define it inside the `tool_addition` block instead of touching `tools`. With `inline-tools-2026-09-15`, the block’s `tool` can be `{"type": "tool_definition", "definition": {...}}`, carrying the same entry you would have put in `tools`:
11941194
11951195```json
11961196{
from line 1217
12171217
12181218The new tool arrives in `messages`, `tools` never changes, and earlier thinking stays valid. Keep at least one tool without `defer_loading: true` in `tools`: if every tool there is deferred, the first tool you define this way costs one full prompt cache miss.
12191219
1220If the API connects to the MCP server for you through the [MCP connector](https://platform.claude.com/docs/en/agents-and-tools/mcp-connector), also send `mcp-client-2026-09-15`. It covers everything `mcp-client-2025-11-20` does, so send it instead of that one. The block's `definition` can then be an `mcp_toolset` for a server listed in `mcp_servers`. When the API has to fetch a server's tool list, the response starts with an `mcp_tool_listing` block for that server. Send it back unchanged with the rest of the assistant turn, and keep sending `mcp-client-2026-09-15` on every later request that carries it. The block pins the toolset to that list, so the API doesn't contact the server again for it. These MCP connector features are available on the Claude API.
1220If the API connects to the MCP server for you through the [MCP connector](https://platform.claude.com/docs/en/agents-and-tools/mcp-connector), also send `mcp-client-2026-09-15`. It covers everything `mcp-client-2025-11-20` does, so send it instead of that one. The block’s `definition` can then be an `mcp_toolset` for a server listed in `mcp_servers`. When the API has to fetch a server’s tool list, the response starts with an `mcp_tool_listing` block for that server. Send it back unchanged with the rest of the assistant turn, and keep sending `mcp-client-2026-09-15` on every later request that carries it. The block pins the toolset to that list, so the API doesn’t contact the server again for it. These MCP connector features are available on the Claude API.
12211221
1222With only the older header, you can still append a tool you learn about mid-session to `tools` with `defer_loading: true`, then offer it with a `tool_addition` block. That's safe because the prefix check ignores a deferred tool until a `tool_addition` block references it. Adding a tool without `defer_loading: true` changes the prefix and invalidates earlier thinking.
1222With only the older header, you can still append a tool you learn about mid-session to `tools` with `defer_loading: true`, then offer it with a `tool_addition` block. That’s safe because the prefix check ignores a deferred tool until a `tool_addition` block references it. Adding a tool without `defer_loading: true` changes the prefix and invalidates earlier thinking.
12231223
12241224The `role: "system"` messages that carry these blocks join the prefix for later thinking. Leave them in place on later requests.
12251225
12261226### Change effort with a per-message `output_config`
12271227
1228Changing top-level `output_config.effort` between requests doesn't invalidate thinking, because effort isn't part of the prefix. Changing top-level effort does restart the prompt cache. Where the model and platform support [per-message effort](https://platform.claude.com/docs/en/build-with-claude/effort#change-effort-mid-conversation-beta), use it instead: append a `role: "system"` message with empty `content` and the new level. It needs the beta header `mid-conversation-output-config-2026-07-01`. On Claude Haiku 5.5, per-message effort is available on the Claude API and Claude Models on Google Cloud (Google managed) and needs adaptive thinking.
1228Changing top-level `output_config.effort` between requests doesn’t invalidate thinking, because effort isn’t part of the prefix. Changing top-level effort does restart the prompt cache. Where the model and platform support [per-message effort](https://platform.claude.com/docs/en/build-with-claude/effort#change-effort-mid-conversation-beta), use it instead: append a `role: "system"` message with empty `content` and the new level. It needs the beta header `mid-conversation-output-config-2026-07-01`. On Claude Haiku 5.5, per-message effort is available on the Claude API and Claude Models on Google Cloud (Google managed) and needs adaptive thinking.
12291229
12301230```json
12311231{ "role": "system", "content": [], "output_config": { "effort": "low" } }
12321232```
12331233
1234When the new level takes effect depends on where you put the message. Directly after a `user` message with new input, it starts with Claude's reply to that message. Anywhere else, such as after an `assistant` message or after a `user` message that holds only `tool_result` blocks, it starts with the next `user` message with new input, so a tool loop already under way keeps the current level until then. Once sent, the message is part of `messages` and therefore part of the prefix for later thinking: leave it in place on later requests, and append another one to change effort again.
1234When the new level takes effect depends on where you put the message. Directly after a `user` message with new input, it starts with Claude’s reply to that message. Anywhere else, such as after an `assistant` message or after a `user` message that holds only `tool_result` blocks, it starts with the next `user` message with new input, so a tool loop already under way keeps the current level until then. Once sent, the message is part of `messages` and therefore part of the prefix for later thinking: leave it in place on later requests, and append another one to change effort again.
12351235
12361236### Trim context on the server
12371237
1238Another common prefix edit is client-side trimming: dropping or summarizing the oldest turns and keeping the recent ones verbatim. The kept turns' thinking blocks were produced while the removed history was still in place, so they fail the check. The server-side equivalents don't count as edits, because the check compares the conversation as you sent it:
1238Another common prefix edit is client-side trimming: dropping or summarizing the oldest turns and keeping the recent ones verbatim. The kept turns’ thinking blocks were produced while the removed history was still in place, so they fail the check. The server-side equivalents don’t count as edits, because the check compares the conversation as you sent it:
12391239
12401240* [On-demand compaction](https://platform.claude.com/docs/en/build-with-claude/compaction-on-demand) (beta) returns the summary from a separate request, which can [run in the background](https://platform.claude.com/docs/en/build-with-claude/compaction-background), and you send the returned block in place of the messages it summarizes. The check accepts that swap, so the turns you keep can stay valid with their thinking, under the [conditions for kept thinking](https://platform.claude.com/docs/en/build-with-claude/compaction-thinking-blocks#conditions-for-kept-thinking-to-stay-valid). [Request a summary](https://platform.claude.com/docs/en/build-with-claude/compaction-on-demand#request-a-summary) shows the request and names the beta header it needs.
1241* [Compaction at a token threshold](https://platform.claude.com/docs/en/build-with-claude/compaction-threshold) summarizes older turns into a compaction block when the context approaches a threshold you set, and the checked prefix restarts from that block. Its [`instructions` parameter](https://platform.claude.com/docs/en/build-with-claude/compaction-threshold#custom-summarization-instructions) takes your own summarization prompt, such as "preserve every ticker, position size, and stated assumption."
1241* [Compaction at a token threshold](https://platform.claude.com/docs/en/build-with-claude/compaction-threshold) summarizes older turns into a compaction block when the context approaches a threshold you set, and the checked prefix restarts from that block. Its [`instructions` parameter](https://platform.claude.com/docs/en/build-with-claude/compaction-threshold#custom-summarization-instructions) takes your own summarization prompt, such as “preserve every ticker, position size, and stated assumption.”
12421242* [Context editing](https://platform.claude.com/docs/en/build-with-claude/context-editing) clears old tool results or old thinking blocks by rule, oldest first. The strategies are `clear_tool_uses_20250919` and `clear_thinking_20251015`.
12431243
12441244### Compact on the client
12451245
1246You can still compact on the client. If you write the summary yourself, don't send back a thinking block that was produced before the rewrite. If the API writes it with on-demand compaction, [Conditions for kept thinking to stay valid](https://platform.claude.com/docs/en/build-with-claude/compaction-thinking-blocks#conditions-for-kept-thinking-to-stay-valid) lists when kept thinking stays valid.
1246You can still compact on the client. If you write the summary yourself, don’t send back a thinking block that was produced before the rewrite. If the API writes it with on-demand compaction, [Conditions for kept thinking to stay valid](https://platform.claude.com/docs/en/build-with-claude/compaction-thinking-blocks#conditions-for-kept-thinking-to-stay-valid) lists when kept thinking stays valid.
12471247
12481248#### Simple compaction (recommended)
12491249
1250When the conversation grows too long, summarize the whole session into one user message and send only that message plus the next instruction. Nothing earlier is replayed, so there's no thinking left to fail the check, and the model reasons afresh from the summary.
1250When the conversation grows too long, summarize the whole session into one user message and send only that message plus the next instruction. Nothing earlier is replayed, so there’s no thinking left to fail the check, and the model reasons afresh from the summary.
12511251
12521252<Frame>
12531253 
from line 1276
12761276 
12771277</Frame>
12781278
1279Fix: keep the turns exactly as they are and send `prefix_mismatch_behavior: "drop_block"`. The API drops the stale thinking blocks, the model reads the kept turns' `text` and `tool_use` blocks, and the request succeeds.
1279Fix: keep the turns exactly as they are and send `prefix_mismatch_behavior: "drop_block"`. The API drops the stale thinking blocks, the model reads the kept turns’ `text` and `tool_use` blocks, and the request succeeds.
12801280
12811281Pass the compacted history as `messages` and set `block_binding` on the `thinking` configuration. In the following example, `compacted_messages` is the array your compaction step produced: the summary message followed by the kept turns exactly as the API returned them, `thinking` blocks included:
12821282
from line 1469
14691469 ```
14701470</CodeGroup>
14711471
1472The response carries the new assistant turn as usual, plus one `input_transformations` entry for each dropped block. For the history in the diagram, that's the thinking on assistant turns 3 and 4:
1472The response carries the new assistant turn as usual, plus one `input_transformations` entry for each dropped block. For the history in the diagram, that’s the thinking on assistant turns 3 and 4:
14731473
14741474```json
14751475{
from line 1488
14881488}
14891489```
14901490
1491Keep sending `"drop_block"` on later requests for as long as those two turns stay in the history. Thinking the model produces from this request onward follows the summary and stays valid. If you'd rather not depend on the beta header, the alternative is to strip the `thinking` and `redacted_thinking` blocks from the kept assistant turns yourself when you build the compacted history.
1491Keep sending `"drop_block"` on later requests for as long as those two turns stay in the history. Thinking the model produces from this request onward follows the summary and stays valid. If you’d rather not depend on the beta header, the alternative is to strip the `thinking` and `redacted_thinking` blocks from the kept assistant turns yourself when you build the compacted history.
14921492
14931493#### Background (async) compaction
14941494
from line 1496
14961496
14971497A summary you build yourself breaks the rule the same way keep-tail does, with a delay: every assistant turn produced while the summary was being built carries thinking that predates the swap, and it all fails the moment the summary lands. If you use one, treat the swap like keep-tail and send `"drop_block"` from the swap onward, or compact synchronously.
14981498
1499#### Patterns that don't work with preserved thinking
1499#### Patterns that don’t work with preserved thinking
15001500
15011501* **Cutting turns out of the middle.** Removing individual turns invalidates every thinking block after them, and no compaction scheme avoids that. If you were cutting a turn to change an instruction, append a [mid-conversation system message](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#new-instructions) instead. To remove old tool results or old thinking selectively, use server-side [context editing](https://platform.claude.com/docs/en/build-with-claude/context-editing).
1502* **Compacting in the middle of a tool round.** Don't compact between an assistant turn's `tool_use` and the `tool_result` that answers it. Send that assistant turn back with its thinking intact so the model completes the round with its reasoning. See [Preserving thinking blocks](https://platform.claude.com/docs/en/build-with-claude/thinking#preserving-thinking-blocks).
1502* **Compacting in the middle of a tool round.** Don’t compact between an assistant turn’s `tool_use` and the `tool_result` that answers it. Send that assistant turn back with its thinking intact so the model completes the round with its reasoning. See [Preserving thinking blocks](https://platform.claude.com/docs/en/build-with-claude/thinking#preserving-thinking-blocks).
15031503
15041504### Reference files by ID, not by a URL whose content changes
15051505
1506For an `image` or `document` block with a `url` source, the check covers the fetched bytes, not the URL string. A URL whose content changes invalidates later thinking: a "latest screenshot" endpoint, or a document someone edits between turns. A rotating signed URL for the same file doesn't. For content you reference across turns, upload it once with the [Files API](https://platform.claude.com/docs/en/build-with-claude/files) and use the `file_id`, or send base64.
1506For an `image` or `document` block with a `url` source, the check covers the fetched bytes, not the URL string. A URL whose content changes invalidates later thinking: a “latest screenshot” endpoint, or a document someone edits between turns. A rotating signed URL for the same file doesn’t. For content you reference across turns, upload it once with the [Files API](https://platform.claude.com/docs/en/build-with-claude/files) and use the `file_id`, or send base64.
15071507
15081508### Libraries, proxies, and gateways
15091509
1510A library, proxy, or gateway sits between someone else's history and the API, so its own rewrites count as edits, and its users can't see or fix them.
1510A library, proxy, or gateway sits between someone else’s history and the API, so its own rewrites count as edits, and its users can’t see or fix them.
15111511
1512* **Pass through what you don't recognize.** Forward the caller's `anthropic-beta` values and `thinking.block_binding` unchanged, and return `input_transformations` to them. An options schema that rejects unknown keys stops your users from choosing `"drop_block"`.
1512* **Pass through what you don’t recognize.** Forward the caller’s `anthropic-beta` values and `thinking.block_binding` unchanged, and return `input_transformations` to them. An options schema that rejects unknown keys stops your users from choosing `"drop_block"`.
15131513* **Leave a `role: "system"` message where the caller put it.** Moving it into the top-level `system` field changes `system` on that request and invalidates every thinking block in the conversation.
1514* **To turn tool use off for a request, send `tool_choice: {"type": "none"}`.** Don't remove `tools`.
1515* **Don't hide the 400.** If your code catches it, strips thinking, and retries on the caller's behalf, log that it did: their history is still edited, and the model loses its earlier reasoning on every later request.
1514* **To turn tool use off for a request, send `tool_choice: {"type": "none"}`.** Don’t remove `tools`.
1515* **Don’t hide the 400.** If your code catches it, strips thinking, and retries on the caller’s behalf, log that it did: their history is still edited, and the model loses its earlier reasoning on every later request.
15161516
15171517## FAQ
15181518
from line 1526
15261526 </Accordion>
15271527
15281528 <Accordion title="Does changing effort or other thinking settings between requests invalidate earlier thinking?">
1529 No. `output_config.effort`, `max_tokens`, and the `thinking` configuration aren't part of the checked prefix, which covers only `system`, `tools`, and `messages`. A top-level effort change invalidates most of the prompt cache. Where the model and platform support per-message effort, a [per-message effort change](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#effort-changes) keeps the prompt cache and is used as the new effort level until changed again.
1529 No. `output_config.effort`, `max_tokens`, and the `thinking` configuration aren’t part of the checked prefix, which covers only `system`, `tools`, and `messages`. A top-level effort change invalidates most of the prompt cache. Where the model and platform support per-message effort, a [per-message effort change](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#effort-changes) keeps the prompt cache and is used as the new effort level until changed again.
15301530 </Accordion>
15311531
15321532 <Accordion title="My tool list changes mid-session. How do I avoid invalidating the conversation?">
1533 Don't edit `tools`. Declare the full set at session start, mark tools that aren't available yet with `defer_loading: true`, and offer or withdraw them with `tool_addition` and `tool_removal` blocks. If you learn a tool's schema only mid-session, define it inside the `tool_addition` block (`inline-tools-2026-09-15`, plus `mcp-client-2026-09-15` for a server the API's MCP connector reaches) and leave `tools` unchanged. The `role: "system"` messages that carry these blocks join the prefix for later thinking, so don't move, reword, or delete them afterward. See [Add or remove tools with `tool_addition` and `tool_removal`](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#tool-changes).
1533 Don’t edit `tools`. Declare the full set at session start, mark tools that aren’t available yet with `defer_loading: true`, and offer or withdraw them with `tool_addition` and `tool_removal` blocks. If you learn a tool’s schema only mid-session, define it inside the `tool_addition` block (`inline-tools-2026-09-15`, plus `mcp-client-2026-09-15` for a server the API’s MCP connector reaches) and leave `tools` unchanged. The `role: "system"` messages that carry these blocks join the prefix for later thinking, so don’t move, reword, or delete them afterward. See [Add or remove tools with `tool_addition` and `tool_removal`](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#tool-changes).
15341534 </Accordion>
15351535
15361536 <Accordion title="I compact by summarizing older turns and keeping recent turns verbatim. Does that still work?">
15371537 Yes, if the API writes the summary. [On-demand compaction](https://platform.claude.com/docs/en/build-with-claude/compaction-on-demand) (beta header `compact-2026-09-04`) summarizes the older turns into a signed block that you send in place of them. The recent turns keep their thinking under the [conditions for kept thinking](https://platform.claude.com/docs/en/build-with-claude/compaction-thinking-blocks#conditions-for-kept-thinking-to-stay-valid).
15381538
1539 If you write the summary yourself, the kept turns' thinking fails the check, because those blocks were produced against the history you replaced. Strip `thinking` and `redacted_thinking` blocks from the turns you carry across and keep their `text` and `tool_use` blocks, or send `prefix_mismatch_behavior: "drop_block"` and let the API drop them. Simple compaction leaves no thinking behind to fail and is the recommended approach: one summary message plus the next user turn, with no earlier turns replayed. Server-side [compaction](https://platform.claude.com/docs/en/build-with-claude/compaction) and [context editing](https://platform.claude.com/docs/en/build-with-claude/context-editing) don't count as edits. See [Compact on the client](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#custom-compaction-on-the-client).
1539 If you write the summary yourself, the kept turns’ thinking fails the check, because those blocks were produced against the history you replaced. Strip `thinking` and `redacted_thinking` blocks from the turns you carry across and keep their `text` and `tool_use` blocks, or send `prefix_mismatch_behavior: "drop_block"` and let the API drop them. Simple compaction leaves no thinking behind to fail and is the recommended approach: one summary message plus the next user turn, with no earlier turns replayed. Server-side [compaction](https://platform.claude.com/docs/en/build-with-claude/compaction) and [context editing](https://platform.claude.com/docs/en/build-with-claude/context-editing) don’t count as edits. See [Compact on the client](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#custom-compaction-on-the-client).
15401540 </Accordion>
15411541
15421542 <Accordion title="How do I handle instruction files such as AGENTS.md or CLAUDE.md that change mid-session?">
1543 Load them once at session start and keep the top-level `system` prompt and `tools` fixed. When a file changes, append the new version at that point in `messages` instead of editing the original. Use a mid-conversation system message for instructions that come from you as the operator. For file text you treat as untrusted, which shouldn't carry system-prompt authority, put the content in the next `user` turn instead. See [Add instructions with a mid-conversation system message](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#new-instructions) and [Limitations](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages#limitations).
1543 Load them once at session start and keep the top-level `system` prompt and `tools` fixed. When a file changes, append the new version at that point in `messages` instead of editing the original. Use a mid-conversation system message for instructions that come from you as the operator. For file text you treat as untrusted, which shouldn’t carry system-prompt authority, put the content in the next `user` turn instead. See [Add instructions with a mid-conversation system message](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#new-instructions) and [Limitations](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages#limitations).
15441544 </Accordion>
15451545
15461546 <Accordion title="Can I resume a saved session later, after a restart or the next day?">
1547 Yes. A resumed session is an ordinary follow-up request: `system`, `tools`, and the earlier `messages` must have the same content as what you last sent. JSON formatting and key order don't matter; the values do. Persist exactly what you sent and received, and replay that: the rendered system prompt, the tool definitions, and each assistant turn as returned. Don't re-render from inputs that might have changed since, such as the date, an updated instruction file, or a new tool version. Anything new goes in an appended message. See [Send assistant turns back exactly as returned](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#append-assistant-turns-exactly-as-returned).
1547 Yes. A resumed session is an ordinary follow-up request: `system`, `tools`, and the earlier `messages` must have the same content as what you last sent. JSON formatting and key order don’t matter; the values do. Persist exactly what you sent and received, and replay that: the rendered system prompt, the tool definitions, and each assistant turn as returned. Don’t re-render from inputs that might have changed since, such as the date, an updated instruction file, or a new tool version. Anything new goes in an appended message. See [Send assistant turns back exactly as returned](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#append-assistant-turns-exactly-as-returned).
15481548 </Accordion>
15491549
15501550 <Accordion title="A saved session now fails on every request. How do I get it working again?">
1551 The stored history has an edit in it, so replaying it can't succeed. Send that session with `prefix_mismatch_behavior: "drop_block"` from now on, or remove its `thinking` and `redacted_thinking` blocks once and continue. On Claude Sonnet 5.5 and Claude Haiku 5.5, `block_binding` works only with `thinking: {"type": "adaptive"}`. With `between_tools` on Claude Sonnet 5.5, or `thinking: {"type": "disabled"}` on Claude Haiku 5.5, keep the history append-only, or strip the thinking blocks from the edited turn on. Thinking the model produces from that point on stays valid as long as nothing before it changes again. Then find the edit so that new sessions don't hit it. See [Handle the error in code](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#handle-the-error-in-code).
1551 The stored history has an edit in it, so replaying it can’t succeed. Send that session with `prefix_mismatch_behavior: "drop_block"` from now on, or remove its `thinking` and `redacted_thinking` blocks once and continue. On Claude Sonnet 5.5 and Claude Haiku 5.5, `block_binding` works only with `thinking: {"type": "adaptive"}`. With `between_tools` on Claude Sonnet 5.5, or `thinking: {"type": "disabled"}` on Claude Haiku 5.5, keep the history append-only, or strip the thinking blocks from the edited turn on. Thinking the model produces from that point on stays valid as long as nothing before it changes again. Then find the edit so that new sessions don’t hit it. See [Handle the error in code](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#handle-the-error-in-code).
15521552 </Accordion>
15531553
1554 <Accordion title="My harness can route a turn to a non-Claude model. Do those turns invalidate Claude's earlier thinking?">
1555 No, provided they're appended after the existing history and nothing earlier changes: an assistant message without thinking blocks is an appended message like any other. Send the other model's output as `text` and `tool_use` content.
1554 <Accordion title="My harness can route a turn to a non-Claude model. Do those turns invalidate Claude’s earlier thinking?">
1555 No, provided they’re appended after the existing history and nothing earlier changes: an assistant message without thinking blocks is an appended message like any other. Send the other model’s output as `text` and `tool_use` content.
15561556 </Accordion>
15571557
1558 <Accordion title="Can I carry a conversation's reasoning into a new conversation?">
1559 Not into a different conversation. A thinking block is usable only when it follows the exact `system`, `tools`, and `messages` it was produced from. A branch that replays that history unchanged up to the fork point keeps its thinking. A conversation that starts from anything else can't use it, so start that conversation from a summary of the task state, as in [simple compaction](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#custom-compaction-on-the-client): the goal, decisions made, files and results so far, and the next step. Thinking blocks from Claude Sonnet 5.5 and Claude Haiku 5.5 also can't be reused from another account (see [Thinking blocks stay with the account that produced them](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#account-bound-thinking)).
1558 <Accordion title="Can I carry a conversation’s reasoning into a new conversation?">
1559 Not into a different conversation. A thinking block is usable only when it follows the exact `system`, `tools`, and `messages` it was produced from. A branch that replays that history unchanged up to the fork point keeps its thinking. A conversation that starts from anything else can’t use it, so start that conversation from a summary of the task state, as in [simple compaction](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#custom-compaction-on-the-client): the goal, decisions made, files and results so far, and the next step. Thinking blocks from Claude Sonnet 5.5 and Claude Haiku 5.5 also can’t be reused from another account (see [Thinking blocks stay with the account that produced them](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#account-bound-thinking)).
15601560 </Accordion>
15611561</AccordionGroup>
15621562
No line in this hunk matches that.