preserved-thinking changedbuild-with-claude/preserved-thinking
Recorded here
Lines+75added
Lines−55removed
From line
1
where the diff opens
First seen
1 Sep 2026
this site's first read of the page
Recorded edits48to this page, all time
The whole hunk
from line 1, old and new numbered
/
from line 1
11---
22title: Preserved thinking
33url: https://platform.claude.com/docs/en/build-with-claude/preserved-thinking
4description: On the latest Claude models, preserved thinking determines whether the model can use a thinking block you send back from an earlier turn.
4description: On Claude models that run the prefix check, preserved thinking determines whether the model can use a thinking block you send back from an earlier turn.
55featureMetadata:
66 status: stable
77 supportedModels:
from line 14
1414 - claude-haiku-5-5
1515 supportedPlatforms:
1616 Claude API: stable
17 Amazon Bedrock: stable
18 Amazon Bedrock (bedrock-runtime): stable
1719 Claude Platform on AWS: stable
1820 Claude Models on Google Cloud (Anthropic managed): stable
19 Amazon Bedrock: stable
2021 Claude Models on Google Cloud (Google managed): stable
2122 Microsoft Foundry: stable
2223---
from line 42
4142
4243On the Claude API, if a request fails the prefix check and leaves [`prefix_mismatch_behavior`](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#mismatch-behavior) unset, whether it gets an error depends on the model and the account:
4344
44| Model | Account created | Account created |
45| ----------------- | --------------------------- | ----------------------------------------- |
46| Name | On or after August 31, 2026 | Before August 31, 2026 |
47| Claude Fable 5.2 | 400 error | 400 error |
48| Claude Mythos 5.2 | 400 error | 400 error |
49| Claude Fable 5.1 | 400 error | No error (failing blocks reach the model) |
50| Claude Mythos 5.1 | 400 error | 400 error |
51| Claude Opus 5.5 | 400 error | No error (failing blocks reach the model) |
52| Claude Sonnet 5.5 | 400 error | No error (failing blocks reach the model) |
53| Claude Haiku 5.5 | 400 error | No error (failing blocks reach the model) |
45| Model | Account created | Account created |
46| ----------------- | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
47| Name | On or after August 31, 2026 | Before August 31, 2026 |
48| Claude Fable 5.2 | Enforced (400 error) | Enforced (400 error) |
49| Claude Mythos 5.2 | Enforced (400 error) | Enforced (400 error) |
50| Claude Fable 5.1 | Enforced (400 error) | [Enforcement varies by account](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#enforcement-details) |
51| Claude Mythos 5.1 | Enforced (400 error) | Enforced (400 error) |
52| Claude Opus 5.5 | Enforced (400 error) | [Enforcement varies by account](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#enforcement-details) |
53| Claude Sonnet 5.5 | Enforced (400 error) | [Enforcement varies by account](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#enforcement-details) |
54| Claude Haiku 5.5 | Enforced (400 error) | [Enforcement varies by account](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#enforcement-details) |
5455
55The cutoff is 00:00 UTC on August 31, 2026. In the Message Batches API, an item that leaves the field unset doesn’t get the 400 error. For this and for how to tell which column your account is in, see [What else happens when `prefix_mismatch_behavior` is unset](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#enforcement-details).
56The dates in the table are at 00:00 UTC.
5657
58Where enforcement varies by account, a request that fails the prefix check can get either a 400 error or no error. When no error is returned, the failing blocks reach the model. To find out which behavior your account gets, [test your account](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#enforcement-details). To get the same result on every account, make your integration append-only or set `prefix_mismatch_behavior`. Otherwise, your code must handle both behaviors.
59
60In the Message Batches API, an item that leaves the field unset doesn’t get the 400 error. For this and for how to tell which outcome your account gets, see [What else happens when `prefix_mismatch_behavior` is unset](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#enforcement-details).
61
5762### What to check in your integration
5863
5964If your code builds or rewrites requests itself, check it if, between two requests in one conversation, it does any of the following:
from line 77
7277| Uses the MCP connector with an unpinned tool list that changes between requests, which affects thinking blocks from the models named in [What counts as an edit](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#what-counts-as-an-edit) | On the Claude API, [pin the server’s tool list](https://platform.claude.com/docs/en/agents-and-tools/mcp-connector#pin-mcp-tool-list). |
7378| Rewrites someone else’s requests as a library, proxy, or gateway between their history and the API | Follow [Libraries, proxies, and gateways](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#libraries-proxies-gateways). |
7479
75A run with no errors on your own API key doesn’t prove that your code keeps the prefix intact. When a request leaves `prefix_mismatch_behavior` unset, failing blocks reach the model without an error wherever the table in [When `prefix_mismatch_behavior` is unset](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#enforcement) shows no error. People who run your tool with their own API keys can get a 400 error even when you don’t. To see the error from your account, set `prefix_mismatch_behavior` to `"error"` on your test requests, as [Check whether your code edits the prefix](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#how-to-tell-whether-your-integration-is-impacted) shows.
80A run with no errors on your own API key doesn’t prove that your code keeps the prefix intact. When a request to Claude Fable 5.1, Claude Opus 5.5, Claude Sonnet 5.5, or Claude Haiku 5.5 leaves `prefix_mismatch_behavior` unset, failing blocks can reach the model without an error, [depending on the account](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#enforcement). People who run your tool with their own API keys can get a 400 error even when you don’t. To see the error from your account, set `prefix_mismatch_behavior` to `"error"` on your test requests, as [Check whether your code edits the prefix](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#how-to-tell-whether-your-integration-is-impacted) shows.
7681
7782Also check your integration if it sends Claude Sonnet 5.5, Claude Haiku 5.5, Claude Fable 5.2, or Claude Mythos 5.2 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).
7883
from line 89
8489* The set of `tools`
8590* Every `message` before the block
8691
87With server-side [compaction](https://platform.claude.com/docs/en/build-with-claude/compaction), the checked prefix starts at the most recent compaction block.
92With [compaction at a token threshold](https://platform.claude.com/docs/en/build-with-claude/compaction-threshold), the API checks `messages` only from the most recent compaction block. It still checks `system` and `tools`.
8893
8994Request 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.
9095
from line 102
97102On the Claude API, the prefix check always runs on these models. Set `thinking.block_binding.prefix_mismatch_behavior` to choose what happens to a block that fails it. 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.
98103
99104* **`"error"`:** The API rejects the request with a 400 `invalid_request_error` that names the first failing block.
100* **`"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"`. It’s a stopgap with an [output-token cost](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#drop-block-cost).
105* **`"drop_block"`:** The API drops each failing block, 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"`. It’s a stopgap with an [output-token cost](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#drop-block-cost).
101106
102107#### What else happens when `prefix_mismatch_behavior` is unset
103108
104The table in [When `prefix_mismatch_behavior` is unset](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#enforcement) gives the outcome for each model and account.
109The table in [When `prefix_mismatch_behavior` is unset](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#enforcement) lists the outcomes for each model and account.
105110
106111A failing block that reaches the model is read and billed like any other input. With the beta header, the response lists each one in `input_transformations` as `thinking_mismatch_allowed`.
107112
108In the Message Batches API, an item that leaves the field unset doesn’t fail: where the table shows a 400 error, the API drops the failing blocks instead. An item that sets the field to `"error"` and sends a failing block resolves as `errored`.
113In the Message Batches API, an item that leaves the field unset doesn’t fail: where the same request outside a batch would get a 400 error, the API drops the failing blocks instead. An item that sets the field to `"error"` and sends a failing block resolves as `errored`.
109114
110To find out which column your account is in, take a Claude Fable 5.1 conversation that contains a thinking block and change something before that block. Send it to Claude Fable 5.1 without the beta header or the `block_binding` field. A 400 error that names the header means your requests get the outcome in the “on or after August 31, 2026” column. A 200 response means they get the outcome in the “before August 31, 2026” column. To confirm a 200 result, send the same request again with the beta header and without `block_binding`: the response lists every thinking block after your edit in `input_transformations` as `thinking_mismatch_allowed`.
115To find out which outcome your account gets, take a Claude Fable 5.1 conversation that contains a thinking block and change something before that block. Send it to Claude Fable 5.1 without the beta header or the `block_binding` field. A 400 error that names the header means that your account gets the error. A 200 response means that it gets no error. To confirm a 200 result, send the same request again with the beta header and without `block_binding`: the response lists every thinking block after your edit in `input_transformations` as `thinking_mismatch_allowed`.
111116
112117#### The 400 error
113118
from line 183
178183 ```
179184
180185 ```bash CLI
181 ant beta:messages create --beta thinking-binding-controls-2026-08-01 \
182 --format yaml <<'YAML'
186 ant beta:messages create --beta thinking-binding-controls-2026-08-01 <<'YAML'
183187 model: claude-fable-5-1
184188 max_tokens: 16000
185189 thinking:
from line 349
345349
346350 ```php PHP
347351 use Anthropic\Beta\AnthropicBeta;
352 use Anthropic\Beta\Messages\BetaTextBlock;
348353 use Anthropic\Beta\Messages\BetaThinkingBlockBinding;
349354 use Anthropic\Beta\Messages\BetaThinkingConfigAdaptive;
350355 use Anthropic\Beta\Messages\BetaThinkingPrefixMismatchBehavior;
351356 use Anthropic\Client;
357 use Anthropic\Messages\Model;
352358
353359 $client = new Client();
354360
355361 $response = $client->beta->messages->create(
356 model: 'claude-fable-5-1',
362 model: Model::CLAUDE_FABLE_5_1,
357363 maxTokens: 16000,
358364 thinking: BetaThinkingConfigAdaptive::with(
359365 blockBinding: BetaThinkingBlockBinding::with(
from line 373
367373 );
368374
369375 foreach ($response->content as $block) {
370 if ($block->type === 'text') {
376 if ($block instanceof BetaTextBlock) {
371377 echo $block->text, PHP_EOL;
372378 }
373379 }
from line 398
392398 )
393399
394400 response.content.each do |block|
395 puts block.text if block.type == :text
401 puts block.text if block.is_a?(Anthropic::Beta::BetaTextBlock)
396402 end
397403
398404 puts "Input transformations: #{response.input_transformations&.length || 0}"
from line 413
407413Under 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:
408414
409415* **`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)).
410* **`thinking_mismatch_allowed`:** The block fails the prefix check, but the API lets it through 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 that leave `prefix_mismatch_behavior` unset, where the table in [When `prefix_mismatch_behavior` is unset](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#enforcement) shows no error. A request that sets the field never gets this entry.
416* **`thinking_mismatch_allowed`:** The block fails the prefix check, but the API lets it through 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 that leave `prefix_mismatch_behavior` unset, on the models and accounts where [failing blocks reach the model](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#enforcement). A request that sets the field never gets this entry.
411417
412418The 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.
413419
from line 421
415421
416422### What counts as an edit
417423
418Each row compares a request with the one before it. “Later thinking blocks” means the blocks already in the history that come after the changed content:
424Each row is a change made after a thinking block was produced. “Later thinking blocks” means the blocks already in the history that come after the changed content:
419425
420426| Change between requests | Later thinking blocks |
421427| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
from line 432
426432| 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)) |
427433| Add, move, or remove `cache_control` markers | Valid |
428434| 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) |
435| [Compaction at a token threshold](https://platform.claude.com/docs/en/build-with-claude/compaction-threshold) or context editing removes or replaces content | Valid (the check compares what you sent, not the server’s edited copy) |
430436| A cleared [turn-scoped system message](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#per-turn-reminders) left in place | Valid |
431437| 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) |
432438| Re-render the context you put in the first user message with a changed value | Invalid for every thinking block |
from line 439
433439| Clear or shorten an earlier `tool_result`, re-encode an earlier image, or change an earlier `tool_use` input | Invalid for every later thinking block |
434440| Add a text block to an earlier user turn, or remove one you added last time | Invalid |
435441| Change the top-level `system` string or blocks | Invalid |
436| Add, remove, rename, or edit a tool in `tools` | Invalid |
442| Add, remove, rename, or edit a tool in `tools` | Invalid, except for a deferred tool that nothing has referenced |
437443| Remove a `thinking` block from the middle of the history and keep later ones | Invalid for every later thinking block |
438444| Put back a `thinking` block you removed on an earlier request | Invalid for thinking blocks produced while it was gone |
439445| An image or document URL that returns different bytes on the next request | Invalid |
from line 456
450456
451457<CodeGroup exclude="shell:cURL, shell:CLI">
452458 <CodeGroupItem language="cURL">
453 This workflow needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
459 This workflow needs more logic than a single shell command. Use the [Anthropic SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
454460 </CodeGroupItem>
455461
456462 <CodeGroupItem language="CLI">
457 This workflow needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
463 This workflow needs more logic than a single shell command. Use the [Anthropic SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
458464 </CodeGroupItem>
459465
460466 ```python Python
from line 642
636642
637643 ```php PHP
638644 use Anthropic\Beta\AnthropicBeta;
645 use Anthropic\Beta\Messages\BetaThinkingBlock;
639646 use Anthropic\Beta\Messages\BetaThinkingBlockBinding;
640647 use Anthropic\Beta\Messages\BetaThinkingConfigAdaptive;
641648 use Anthropic\Beta\Messages\BetaThinkingPrefixMismatchBehavior;
642649 use Anthropic\Client;
650 use Anthropic\Messages\Model;
643651
644652 $client = new Client();
645653
from line 661
653661 foreach ($userTurns as $userTurn) {
654662 $messages[] = ['role' => 'user', 'content' => $userTurn];
655663 $response = $client->beta->messages->create(
656 model: 'claude-fable-5-1',
664 model: Model::CLAUDE_FABLE_5_1,
657665 maxTokens: 16000,
658666 thinking: BetaThinkingConfigAdaptive::with(
659667 blockBinding: BetaThinkingBlockBinding::with(
from line 672
664672 betas: [AnthropicBeta::THINKING_BINDING_CONTROLS_2026_08_01],
665673 );
666674 $messages[] = ['role' => 'assistant', 'content' => $response->content];
667 $thinkingBlocks = array_filter($response->content, fn ($block) => $block->type === 'thinking');
675 $thinkingBlocks = array_filter($response->content, fn ($block) => $block instanceof BetaThinkingBlock);
668676 $dropped = $response->inputTransformations ?? [];
669677 echo 'thinking blocks: ', count($thinkingBlocks), ', dropped: ', count($dropped), PHP_EOL;
670678 }
from line 734
726734* **`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).
727735* **`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).
728736
729To 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 error 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, claude-fable-5-2, or claude-mythos-5-2, that the request sets `block_binding`, that the history you sent contains `thinking` blocks, and that the first two requests had no `system` prompt.
737To 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 error 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 runs the [prefix check](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#prefix-check), that the request sets `block_binding`, that the history you sent contains `thinking` blocks, and that the first two requests had no `system` prompt.
730738
731739Two plain turns rarely show the problem. Run a session through each of the following, with `"error"` set so that a regression fails your CI:
732740
from line 745
737745* A switch to another model and back
738746* A save, a restart, and a resume on a later date
739747
740Where the table in [When `prefix_mismatch_behavior` is unset](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#enforcement) shows no error, you can also watch production traffic: 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 pass. An edit to `system` or `tools` comes before every block, so every thinking block in the request fails the check. One entry looks like this:
748If [failing blocks reach the model](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#enforcement) for your requests, you can also watch production traffic: 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 already in the history 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 pass. An edit to `system` or `tools` comes before every block, so every thinking block already in the history fails the check. One entry looks like this:
741749
742750```json
743751{
from line 759
751759}
752760```
753761
754Run the following example from an [account created before August 31, 2026](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#enforcement), because on an account created on or after that date, the API rejects its third request with a 400 error. Keep the example on Claude Fable 5.1: on Claude Fable 5.2 and Claude Mythos 5.2, the third request gets the 400 error on every account. It sends the header without `block_binding` and extends the earlier session with a third request that adds a system prompt, which changes the prefix on purpose. After each turn it prints the number of `thinking` blocks and flagged blocks:
762Run the following example on Claude Fable 5.1 from an account where [failing blocks reach the model](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#enforcement). On any other account, the API rejects its third request with a 400 error. On Claude Fable 5.2 and Claude Mythos 5.2, the third request gets the 400 error on every account. The example sends the header without `block_binding` and extends the earlier session with a third request that adds a system prompt, which changes the prefix on purpose. After each turn it prints the number of `thinking` blocks and flagged blocks:
755763
756764<CodeGroup exclude="shell:cURL, shell:CLI">
757765 <CodeGroupItem language="cURL">
758 This workflow needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
766 This workflow needs more logic than a single shell command. Use the [Anthropic SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
759767 </CodeGroupItem>
760768
761769 <CodeGroupItem language="CLI">
762 This workflow needs more logic than a single shell command. Use the [Claude SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
770 This workflow needs more logic than a single shell command. Use the [Anthropic SDK](https://platform.claude.com/docs/en/cli-sdks-libraries/overview) to run it.
763771 </CodeGroupItem>
764772
765773 ```python Python
from line 882
874882
875883 // messages grows across turns: each assistant turn goes back exactly as returned
876884 messages := []anthropic.BetaMessageParam{}
877 for i, userTurn := range userTurns {
885 for turnIndex, userTurn := range userTurns {
878886 messages = append(messages, anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock(userTurn)))
879887 // Only the last request adds a system prompt, which changes the prefix on purpose
880888 var system []anthropic.BetaTextBlockParam
881 if i == len(userTurns)-1 {
889 if turnIndex == len(userTurns)-1 {
882890 system = []anthropic.BetaTextBlockParam{{Text: "Answer briefly."}}
883891 }
884892 response, err := client.Beta.Messages.New(context.TODO(), anthropic.BetaMessageNewParams{
from line 965
957965
958966 ```php PHP
959967 use Anthropic\Beta\AnthropicBeta;
968 use Anthropic\Beta\Messages\BetaThinkingBlock;
960969 use Anthropic\Beta\Messages\BetaThinkingConfigAdaptive;
961970 use Anthropic\Beta\Messages\BetaThinkingMismatchAllowedInputTransformation;
962971 use Anthropic\Client;
972 use Anthropic\Messages\Model;
963973
964974 $client = new Client();
965975
from line 984
974984 foreach ($userTurns as $turnIndex => $userTurn) {
975985 $messages[] = ['role' => 'user', 'content' => $userTurn];
976986 $response = $client->beta->messages->create(
977 model: 'claude-fable-5-1',
987 model: Model::CLAUDE_FABLE_5_1,
978988 maxTokens: 16000,
979989 thinking: BetaThinkingConfigAdaptive::with(),
980990 // Only the last request adds a system prompt, which changes the prefix on purpose
from line 993
983993 betas: [AnthropicBeta::THINKING_BINDING_CONTROLS_2026_08_01],
984994 );
985995 $messages[] = ['role' => 'assistant', 'content' => $response->content];
986 $thinkingBlocks = array_filter($response->content, fn ($block) => $block->type === 'thinking');
996 $thinkingBlocks = array_filter($response->content, fn ($block) => $block instanceof BetaThinkingBlock);
987997 $flagged = array_filter(
988998 $response->inputTransformations ?? [],
989999 fn ($transformation) => $transformation instanceof BetaThinkingMismatchAllowedInputTransformation,
from line 1043
10331043
10341044The 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.
10351045
1036Handle 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). If the request sets `prefix_mismatch_behavior`, or if the table in [When `prefix_mismatch_behavior` is unset](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#enforcement) shows a 400 error for your model and account, the API instead rejects the request or drops the failing blocks. The entries are a lower bound on what `"drop_block"` 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 lets failing blocks through, it evaluates each block on its own and lists a block only if that block fails.
1046Handle these entries as you would `prefix_binding_mismatch` drops. The edit is before the first block listed. To find it, diff `system`, `tools`, and `messages` up to that block’s `path` against the conversation as it stood when Claude produced that block. Then replace the 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). If the request sets `prefix_mismatch_behavior`, or if [your model and account get the 400 error](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#enforcement), the API instead rejects the request or drops the failing blocks. The entries are a lower bound on what `"drop_block"` would remove. With `"drop_block"`, a failing block also takes the rest of that turn’s consecutive thinking blocks with it, and removing one block can make the next one fail too. When the API lets failing blocks through, it evaluates each block on its own and lists a block only if that block fails.
10371047
10381048## Make changes without editing the prefix
10391049
from line 1230
12201230
12211231If 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`](https://platform.claude.com/docs/en/build-with-claude/mid-conversation-system-messages#add-an-mcp-server-mid-conversation-beta). 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.
12221232
1223With only the `mid-conversation-tool-changes-2026-07-01` header, you can 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.
1233With only the `mid-conversation-tool-changes-2026-07-01` header, you can 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 something in the conversation references it. Adding a tool without `defer_loading: true` changes the prefix and invalidates earlier thinking.
12241234
12251235The `role: "system"` messages that carry these blocks join the prefix for later thinking. Leave them in place on later requests.
12261236
12271237### Change effort with a per-message `output_config`
12281238
1229Changing 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 needs adaptive thinking.
1239Changing 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-per-message), 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 Sonnet 5.5 and Claude Haiku 5.5, a per-message effort change needs adaptive thinking.
12301240
12311241```json
12321242{ "role": "system", "content": [], "output_config": { "effort": "low" } }
from line 1246
12361246
12371247### Trim context on the server
12381248
1239Another 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:
1249Another 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 avoid that, because the check compares the conversation as you sent it:
12401250
12411251* [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.
1242* [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.”
1252* [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 check on `messages` 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.”
12431253* [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`.
12441254
12451255### Compact on the client
from line 1442
14321442 use Anthropic\Beta\Messages\BetaThinkingConfigAdaptive;
14331443 use Anthropic\Beta\Messages\BetaThinkingPrefixMismatchBehavior;
14341444 use Anthropic\Client;
1445 use Anthropic\Messages\Model;
14351446
14361447 $client = new Client();
14371448
14381449 // $compactedMessages: the summary message, then the kept turns as returned
14391450 $response = $client->beta->messages->create(
1440 model: 'claude-fable-5-1',
1451 model: Model::CLAUDE_FABLE_5_1,
14411452 maxTokens: 16000,
14421453 thinking: BetaThinkingConfigAdaptive::with(
14431454 blockBinding: BetaThinkingBlockBinding::with(
from line 1467
14561467
14571468 # compacted_messages: the summary message, then the kept turns as returned
14581469 response = client.beta.messages.create(
1459 model: "claude-fable-5-1",
1470 model: Anthropic::Model::CLAUDE_FABLE_5_1,
14601471 max_tokens: 16_000,
14611472 thinking: {
14621473 type: "adaptive",
from line 1554
15431554
15441555#### Earlier models
15451556
1546A conversation that moves from Claude Sonnet 5 or an earlier Opus, Sonnet, or Haiku model to Claude Opus 5, Claude Opus 5.5, Claude Sonnet 5.5, Claude Haiku 5.5, or any Claude Fable or Claude Mythos model keeps its reasoning. The reverse doesn’t hold: these earlier models can’t read thinking blocks from those models. Claude Opus 5, Claude Fable 5, and Claude Mythos 5 can’t read thinking blocks from Claude Fable 5.2, Claude Mythos 5.2, Claude Fable 5.1, Claude Mythos 5.1, Claude Opus 5.5, Claude Sonnet 5.5, or Claude Haiku 5.5.
1557Claude Sonnet 5 and earlier Opus, Sonnet, and Haiku models are the earlier models in this section. A conversation that moves from one of them to Claude Opus 5, Claude Opus 5.5, Claude Sonnet 5.5, Claude Haiku 5.5, or any Claude Fable or Claude Mythos model keeps its reasoning. The reverse doesn’t hold: the earlier models can’t read thinking blocks from those models. Claude Opus 5, Claude Fable 5, and Claude Mythos 5 can’t read thinking blocks from these models:
15471558
1559* Claude Fable 5.2 and Claude Mythos 5.2
1560* Claude Fable 5.1 and Claude Mythos 5.1
1561* Claude Opus 5.5
1562* Claude Sonnet 5.5
1563* Claude Haiku 5.5
1564
1565Claude Opus 5 also can’t read thinking blocks from Claude Fable 5 or Claude Mythos 5.
1566
15481567### See which blocks were dropped
15491568
15501569With 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 a top-level `input_transformations` array with `reason: "model_binding_mismatch"`:
from line 1594
15751594
15761595<AccordionGroup>
15771596 <Accordion title="Can I test preserved thinking from an account created before August 31, 2026?">
1578 Yes. Send the `thinking-binding-controls-2026-08-01` beta header and set `thinking.block_binding.prefix_mismatch_behavior`. The setting applies to that request on any account: `"error"` rejects an edited history with a 400 error, and `"drop_block"` lets the request through and lists what was dropped in `input_transformations`. See [Check whether your code edits the prefix](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#how-to-tell-whether-your-integration-is-impacted). Without that setting, what an account created before that date gets varies by model: see the table in [When `prefix_mismatch_behavior` is unset](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#enforcement).
1597 Yes. Send the `thinking-binding-controls-2026-08-01` beta header and set `thinking.block_binding.prefix_mismatch_behavior`. The setting applies to that request on any account: `"error"` rejects an edited history with a 400 error, and `"drop_block"` lets the request through and lists what was dropped in `input_transformations`. See [Check whether your code edits the prefix](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#how-to-tell-whether-your-integration-is-impacted). Without that setting, the outcome depends on the model and the account: see the table in [When `prefix_mismatch_behavior` is unset](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#enforcement).
15791598 </Accordion>
15801599
15811600 <Accordion title="If anything before a thinking block changes, even one tool description, is the conversation unusable?">
from line 1612
15931612 <Accordion title="I compact by summarizing older turns and keeping recent turns verbatim. Does that still work?">
15941613 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).
15951614
1596 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).
1615 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. [Compaction at a token threshold](https://platform.claude.com/docs/en/build-with-claude/compaction-threshold) and [context editing](https://platform.claude.com/docs/en/build-with-claude/context-editing) avoid that failure, because the check compares the conversation as you sent it. See [Compact on the client](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#custom-compaction-on-the-client).
15971616 </Accordion>
15981617
15991618 <Accordion title="How do I handle instruction files such as AGENTS.md or CLAUDE.md that change mid-session?">
from line 1620
16011620 </Accordion>
16021621
16031622 <Accordion title="A saved session fails on every request. How do I get it working again?">
1604 The stored history has an edit in it, so sending it back as it is fails every time, even after you fix the code that made the edit. To continue the session, send the beta header with `prefix_mismatch_behavior: "drop_block"` on every later request. Alternatively, strip the block the error names and every later `thinking` and `redacted_thinking` block, then retry once. If you can’t send `block_binding` (on Claude Sonnet 5.5 and Claude Haiku 5.5, it works only with `thinking: {"type": "adaptive"}`), strip the blocks. Thinking the model produces after that stays valid as long as nothing before it changes again. Then fix the code that made 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).
1623 If the stored history has an edit in it, sending it back as it is fails every time, even after you fix the code that made the edit. To continue the session, send the beta header with `prefix_mismatch_behavior: "drop_block"` on every later request. Alternatively, strip the block the error names and every later `thinking` and `redacted_thinking` block, leave them out of later requests, and retry. If you can’t send `block_binding` (on Claude Sonnet 5.5 and Claude Haiku 5.5, it works only with `thinking: {"type": "adaptive"}`), strip the blocks. Thinking the model produces after that stays valid as long as nothing before it changes again. Then fix the code that made 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).
16051624 </Accordion>
16061625
16071626 <Accordion title="My harness can route a turn to a non-Claude model. Do those turns invalidate Claude’s earlier thinking?">
from line 1665
16461665| Status | Cloud platforms | Models |
16471666| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
16481667| Available | Claude API, [Claude Platform on AWS](https://platform.claude.com/docs/en/build-with-claude/claude-platform-on-aws), [Claude Models on Google Cloud (Google managed)](https://platform.claude.com/docs/en/build-with-claude/claude-models-on-google-cloud/google-managed), [Microsoft Foundry](https://platform.claude.com/docs/en/build-with-claude/claude-in-microsoft-foundry) | [Claude Fable 5.2](https://platform.claude.com/docs/en/models/fable-5-2/overview), [Claude Mythos 5.2](https://platform.claude.com/docs/en/models/mythos-5-2/overview), [Claude Fable 5.1](https://platform.claude.com/docs/en/models/fable-5-1/overview), [Claude Mythos 5.1](https://platform.claude.com/docs/en/models/mythos-5-1/overview), [Claude Opus 5.5](https://platform.claude.com/docs/en/models/opus-5-5/overview), [Claude Sonnet 5.5](https://platform.claude.com/docs/en/models/sonnet-5-5/overview), [Claude Haiku 5.5](https://platform.claude.com/docs/en/models/haiku-5-5/overview) |
1649| Available | [Amazon Bedrock](https://platform.claude.com/docs/en/build-with-claude/claude-in-amazon-bedrock) | [Claude Fable 5.2](https://platform.claude.com/docs/en/models/fable-5-2/overview), [Claude Mythos 5.2](https://platform.claude.com/docs/en/models/mythos-5-2/overview) |
1668| Available | [Amazon Bedrock](https://platform.claude.com/docs/en/build-with-claude/claude-in-amazon-bedrock) | [Claude Fable 5.1](https://platform.claude.com/docs/en/models/fable-5-1/overview), [Claude Opus 5.5](https://platform.claude.com/docs/en/models/opus-5-5/overview), [Claude Sonnet 5.5](https://platform.claude.com/docs/en/models/sonnet-5-5/overview), [Claude Haiku 5.5](https://platform.claude.com/docs/en/models/haiku-5-5/overview) |
1669| Available | [Amazon Bedrock (bedrock-runtime)](https://platform.claude.com/docs/en/build-with-claude/claude-on-amazon-bedrock-legacy) | [Claude Fable 5.2](https://platform.claude.com/docs/en/models/fable-5-2/overview), [Claude Mythos 5.2](https://platform.claude.com/docs/en/models/mythos-5-2/overview) |
16501670| Available | [Claude Models on Google Cloud (Anthropic managed)](https://platform.claude.com/docs/en/build-with-claude/claude-models-on-google-cloud/anthropic-managed) | [Claude Fable 5.2](https://platform.claude.com/docs/en/models/fable-5-2/overview), [Claude Mythos 5.2](https://platform.claude.com/docs/en/models/mythos-5-2/overview) |
16511671
16521672#### [Managed Agents](https://platform.claude.com/docs/en/managed-agents/overview)
No line in this hunk matches that.