Migrating to Claude Fable 5.1 from Claude Fable 5
Migration is mostly drop-in. The API surface, limits, per-token pricing, tokenizer, always-on adaptive thinking, refusal handling, and stop_details categories all match Claude Fable 5. What changes: forced tool choice returns a 400 error, thinking blocks are preserved only for the model that produced them or a newer one and only in the conversation that produced them, cache reads cost less, and agent-loop behavior differs in three ways. The same changes apply to Claude Mythos 5.1, except the conversation check on thinking blocks, which Claude Mythos 5.1 doesn't run.
Update your model name
model = "claude-fable-5" # Before
model = "claude-fable-5-1" # After
# Or, for the Project Glasswing model with the same capabilities:
model = "claude-mythos-5-1" # AfterBreaking changes
Forced tool choice is not supported: Claude Fable 5 accepts
tool_choiceauto,none,any, andtool. Onclaude-fable-5-1,\{type: "any"\}and\{type: "tool", name: "..."\}return a 400invalid_request_error:texttool_choice: type "tool" and "any" are not supported for this model.The check applies on the Messages API, the Message Batches API, and the token counting endpoint.
Before (Claude Fable 5):
bashcurl -sS https://api.anthropic.com/v1/messages \ -H "content-type: application/json" \ -H "x-api-key: $ANTHROPIC_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -d @- <<'EOF' { "model": "claude-fable-5", "max_tokens": 16000, "tools": [ { "name": "record_summary", "description": "Record the structured summary of the document.", "input_schema": { "type": "object", "properties": {"summary": {"type": "string"}}, "required": ["summary"] } } ], "tool_choice": {"type": "tool", "name": "record_summary"}, "messages": [ {"role": "user", "content": "Summarize: The meeting moved to Thursday."} ] } EOF```bash CLI ant messages create < request.yaml ``` ```yaml model: claude-fable-5 max_tokens: 16000 tools: - name: record_summary description: Record the structured summary of the document. input_schema: type: object properties: summary: type: string required: [summary] tool_choice: type: tool name: record_summary messages: - role: user content: "Summarize: The meeting moved to Thursday." ```pythonclient = anthropic.Anthropic() record_summary_tool = { "name": "record_summary", "description": "Record the structured summary of the document.", "input_schema": { "type": "object", "properties": {"summary": {"type": "string"}}, "required": ["summary"], }, } response = client.messages.create( model="claude-fable-5", max_tokens=16000, tools=[record_summary_tool], tool_choice={"type": "tool", "name": "record_summary"}, messages=[{"role": "user", "content": "Summarize: The meeting moved to Thursday."}], ) print(response.content)typescriptconst client = new Anthropic(); const response = await client.messages.create({ model: "claude-fable-5", max_tokens: 16000, tools: [ { name: "record_summary", description: "Record the structured summary of the document.", input_schema: { type: "object", properties: { summary: { type: "string" } }, required: ["summary"] } } ], tool_choice: { type: "tool", name: "record_summary" }, messages: [{ role: "user", content: "Summarize: The meeting moved to Thursday." }] }); console.log(response.content);csharpAnthropicClient client = new(); var parameters = new MessageCreateParams { Model = Model.ClaudeFable5, MaxTokens = 16000, Tools = [ new ToolUnion(new Tool() { Name = "record_summary", Description = "Record the structured summary of the document.", InputSchema = new InputSchema() { Properties = new Dictionary<string, JsonElement> { ["summary"] = JsonSerializer.SerializeToElement(new { type = "string" }), }, Required = ["summary"], }, }), ], ToolChoice = new ToolChoiceTool { Name = "record_summary" }, Messages = [ new() { Role = Role.User, Content = "Summarize: The meeting moved to Thursday." } ] }; var message = await client.Messages.Create(parameters); Console.WriteLine(message);goclient := anthropic.NewClient() response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{ Model: anthropic.ModelClaudeFable5, MaxTokens: 16000, Tools: []anthropic.ToolUnionParam{ {OfTool: &anthropic.ToolParam{ Name: "record_summary", Description: anthropic.String("Record the structured summary of the document."), InputSchema: anthropic.ToolInputSchemaParam{ Properties: map[string]any{ "summary": map[string]any{"type": "string"}, }, Required: []string{"summary"}, }, }}, }, ToolChoice: anthropic.ToolChoiceUnionParam{OfTool: &anthropic.ToolChoiceToolParam{Name: "record_summary"}}, Messages: []anthropic.MessageParam{ anthropic.NewUserMessage(anthropic.NewTextBlock("Summarize: The meeting moved to Thursday.")), }, }) if err != nil { log.Fatal(err) } fmt.Println(response.RawJSON())javavoid main() { AnthropicClient client = AnthropicOkHttpClient.fromEnv(); MessageCreateParams params = MessageCreateParams.builder() .model(Model.CLAUDE_FABLE_5) .maxTokens(16000L) .addTool(Tool.builder() .name("record_summary") .description("Record the structured summary of the document.") .inputSchema(InputSchema.builder() .properties(JsonValue.from(Map.of("summary", Map.of("type", "string")))) .required(List.of("summary")) .build()) .build()) .toolChoice(ToolChoice.ofTool(ToolChoiceTool.builder() .name("record_summary") .build())) .addUserMessage("Summarize: The meeting moved to Thursday.") .build(); Message response = client.messages().create(params); IO.println(response); }php$client = new Client(); $message = $client->messages->create( maxTokens: 16000, messages: [ ['role' => 'user', 'content' => 'Summarize: The meeting moved to Thursday.'] ], model: 'claude-fable-5', toolChoice: ['type' => 'tool', 'name' => 'record_summary'], tools: [ [ 'name' => 'record_summary', 'description' => 'Record the structured summary of the document.', 'input_schema' => [ 'type' => 'object', 'properties' => [ 'summary' => ['type' => 'string'] ], 'required' => ['summary'] ] ] ], ); echo $message;rubyclient = Anthropic::Client.new message = client.messages.create( model: Anthropic::Model::CLAUDE_FABLE_5, max_tokens: 16000, tools: [ { name: "record_summary", description: "Record the structured summary of the document.", input_schema: { type: "object", properties: { summary: { type: "string" } }, required: ["summary"] } } ], tool_choice: { type: "tool", name: "record_summary" }, messages: [ { role: "user", content: "Summarize: The meeting moved to Thursday." } ] ) puts messageAfter (Claude Fable 5.1): leave
tool_choiceatauto, name the tool in the instruction, and setstrict: trueso the call matches your schema. (In a CMEK organization, where structured outputs, includingstrict: true, are not available on Claude Fable models, rely on the instruction alone.) For example:bashcurl -sS https://api.anthropic.com/v1/messages \ -H "content-type: application/json" \ -H "x-api-key: $ANTHROPIC_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -d @- <<'EOF' \{ "model": "claude-fable-5-1", "max_tokens": 16000, "tools": [ \{ "name": "record_summary", "description": "Record the structured summary of the document.", "strict": true, "input_schema": \{ "type": "object", "properties": \{"summary": \{"type": "string"}}, "required": ["summary"], "additionalProperties": false \} \} ], "tool_choice": \{"type": "auto"\}, "messages": [ \{"role": "user", "content": "Summarize: The meeting moved to Thursday. Call the record_summary tool with your result."\} ] \} EOF```bash CLI ant messages create < request.yaml ``` ```yaml model: claude-fable-5-1 max_tokens: 16000 tools: - name: record_summary description: Record the structured summary of the document. strict: true input_schema: type: object properties: summary: type: string required: [summary] additionalProperties: false tool_choice: type: auto messages: - role: user content: "Summarize: The meeting moved to Thursday. Call the record_summary tool with your result." ```pythonclient = anthropic.Anthropic() record_summary_tool = \{ "name": "record_summary", "description": "Record the structured summary of the document.", "strict": True, "input_schema": \{ "type": "object", "properties": \{"summary": \{"type": "string"}}, "required": ["summary"], "additionalProperties": False, \}, \} response = client.messages.create( model="claude-fable-5-1", max_tokens=16000, tools=[record_summary_tool], tool_choice=\{"type": "auto"\}, messages=[ \{ "role": "user", "content": "Summarize: The meeting moved to Thursday. Call the record_summary tool with your result.", \} ], ) print(response.content)typescriptconst client = new Anthropic(); const response = await client.messages.create(\{ model: "claude-fable-5-1", max_tokens: 16000, tools: [ \{ name: "record_summary", description: "Record the structured summary of the document.", strict: true, input_schema: \{ type: "object", properties: \{ summary: \{ type: "string" \} \}, required: ["summary"], additionalProperties: false \} \} ], tool_choice: \{ type: "auto" \}, messages: [ \{ role: "user", content: "Summarize: The meeting moved to Thursday. Call the record_summary tool with your result." \} ] \}); console.log(response.content);csharpAnthropicClient client = new(); var parameters = new MessageCreateParams \{ Model = "claude-fable-5-1", MaxTokens = 16000, Tools = [ new ToolUnion(new Tool() \{ Name = "record_summary", Description = "Record the structured summary of the document.", Strict = true, InputSchema = new InputSchema(new Dictionary<string, JsonElement> \{ ["properties"] = JsonSerializer.SerializeToElement(new Dictionary<string, object> \{ ["summary"] = new \{ type = "string" \}, \}), ["required"] = JsonSerializer.SerializeToElement(new[] \{ "summary" \}), ["additionalProperties"] = JsonSerializer.SerializeToElement(false), \}), \}), ], ToolChoice = new ToolChoiceAuto(), Messages = [ new() \{ Role = Role.User, Content = "Summarize: The meeting moved to Thursday. Call the record_summary tool with your result." \} ] \}; var message = await client.Messages.Create(parameters); Console.WriteLine(message);goclient := anthropic.NewClient() response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams\{ Model: "claude-fable-5-1", MaxTokens: 16000, Tools: []anthropic.ToolUnionParam\{ \{OfTool: &anthropic.ToolParam\{ Name: "record_summary", Description: anthropic.String("Record the structured summary of the document."), Strict: anthropic.Bool(true), InputSchema: anthropic.ToolInputSchemaParam\{ Properties: map[string]any\{ "summary": map[string]any\{"type": "string"\}, \}, Required: []string\{"summary"\}, ExtraFields: map[string]any\{ "additionalProperties": false, \}, \}, }}, \}, ToolChoice: anthropic.ToolChoiceUnionParam\{OfAuto: &anthropic.ToolChoiceAutoParam\{}}, Messages: []anthropic.MessageParam\{ anthropic.NewUserMessage(anthropic.NewTextBlock("Summarize: The meeting moved to Thursday. Call the record_summary tool with your result.")), \}, \}) if err != nil \{ log.Fatal(err) \} fmt.Println(response.RawJSON())javavoid main() \{ AnthropicClient client = AnthropicOkHttpClient.fromEnv(); MessageCreateParams params = MessageCreateParams.builder() .model("claude-fable-5-1") .maxTokens(16000L) .addTool(Tool.builder() .name("record_summary") .description("Record the structured summary of the document.") .inputSchema(InputSchema.builder() .properties(JsonValue.from(Map.of("summary", Map.of("type", "string")))) .putAdditionalProperty("required", JsonValue.from(List.of("summary"))) .putAdditionalProperty("additionalProperties", JsonValue.from(false)) .build()) .strict(true) .build()) .toolChoice(ToolChoice.ofAuto(ToolChoiceAuto.builder().build())) .addUserMessage("Summarize: The meeting moved to Thursday. Call the record_summary tool with your result.") .build(); Message response = client.messages().create(params); IO.println(response); \}php$client = new Client(); $message = $client->messages->create( maxTokens: 16000, messages: [ ['role' => 'user', 'content' => 'Summarize: The meeting moved to Thursday. Call the record_summary tool with your result.'] ], model: 'claude-fable-5-1', toolChoice: ['type' => 'auto'], tools: [ [ 'name' => 'record_summary', 'description' => 'Record the structured summary of the document.', 'strict' => true, 'input_schema' => [ 'type' => 'object', 'properties' => [ 'summary' => ['type' => 'string'] ], 'required' => ['summary'], 'additionalProperties' => false ] ] ], ); echo $message;rubyclient = Anthropic::Client.new message = client.messages.create( model: "claude-fable-5-1", max_tokens: 16000, tools: [ \{ name: "record_summary", description: "Record the structured summary of the document.", strict: true, input_schema: \{ type: "object", properties: \{ summary: \{ type: "string" \} \}, required: ["summary"], additionalProperties: false \} \} ], tool_choice: \{ type: "auto" \}, messages: [ \{ role: "user", content: "Summarize: The meeting moved to Thursday. Call the record_summary tool with your result." \} ] ) puts messageSee Strict tool use and Forcing tool use. If you forced a tool only to get schema-conformant JSON, use JSON outputs (
output_config.format) instead.If your application, rather than the user, requires a specific tool call on the current turn of a multi-turn conversation, append a mid-conversation system message after the latest
userturn. Name the tool, say the call is required for this turn, and tell Claude to open its response with it. Because the message is appended rather than written into the top-levelsystemprompt, earlier turns stay byte-identical and keep their prompt cache hits:bashcurl -sS https://api.anthropic.com/v1/messages \ -H "content-type: application/json" \ -H "x-api-key: $ANTHROPIC_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -d @- <<'EOF' { "model": "claude-fable-5-1", "max_tokens": 16000, "system": "You are a customer support assistant for an online electronics store.", "tools": [ { "name": "search_help_center", "description": "Search the help center for policy and troubleshooting articles.", "strict": true, "input_schema": { "type": "object", "properties": {"query": {"type": "string"}}, "required": ["query"], "additionalProperties": false } } ], "messages": [ {"role": "user", "content": "My headphones from order A1234 arrived yesterday."}, {"role": "assistant", "content": "Thanks for confirming. How can I help with order A1234?"}, {"role": "user", "content": "I opened the box. Can I still return them?"}, { "role": "system", "content": "Tool-use requirement for the current turn: the application requires a call to the search_help_center tool in your response to the user's latest message. Begin your response with the search_help_center tool call. Do not reply with text only." } ] } EOF```bash CLI ant messages create < request.yaml ``` ```yaml model: claude-fable-5-1 max_tokens: 16000 system: You are a customer support assistant for an online electronics store. tools: - name: search_help_center description: Search the help center for policy and troubleshooting articles. strict: true input_schema: type: object properties: query: type: string required: [query] additionalProperties: false messages: - role: user content: My headphones from order A1234 arrived yesterday. - role: assistant content: Thanks for confirming. How can I help with order A1234? - role: user content: I opened the box. Can I still return them? - role: system content: >- Tool-use requirement for the current turn: the application requires a call to the search_help_center tool in your response to the user's latest message. Begin your response with the search_help_center tool call. Do not reply with text only. ```pythonclient = anthropic.Anthropic() search_help_center_tool = { "name": "search_help_center", "description": "Search the help center for policy and troubleshooting articles.", "strict": True, "input_schema": { "type": "object", "properties": {"query": {"type": "string"}}, "required": ["query"], "additionalProperties": False, }, } response = client.messages.create( model="claude-fable-5-1", max_tokens=16000, system="You are a customer support assistant for an online electronics store.", tools=[search_help_center_tool], messages=[ { "role": "user", "content": "My headphones from order A1234 arrived yesterday.", }, { "role": "assistant", "content": "Thanks for confirming. How can I help with order A1234?", }, {"role": "user", "content": "I opened the box. Can I still return them?"}, # The application requires a help center lookup before any policy # answer. Appending the requirement as a system message leaves the # earlier turns unchanged. { "role": "system", "content": "Tool-use requirement for the current turn: the application requires a call to the search_help_center tool in your response to the user's latest message. Begin your response with the search_help_center tool call. Do not reply with text only.", }, ], ) print(response.content)typescriptconst client = new Anthropic(); const response = await client.messages.create({ model: "claude-fable-5-1", max_tokens: 16000, system: "You are a customer support assistant for an online electronics store.", tools: [ { name: "search_help_center", description: "Search the help center for policy and troubleshooting articles.", strict: true, input_schema: { type: "object", properties: { query: { type: "string" } }, required: ["query"], additionalProperties: false } } ], messages: [ { role: "user", content: "My headphones from order A1234 arrived yesterday." }, { role: "assistant", content: "Thanks for confirming. How can I help with order A1234?" }, { role: "user", content: "I opened the box. Can I still return them?" }, // The application requires a help center lookup before any policy // answer. Appending the requirement as a system message leaves the // earlier turns unchanged. { role: "system", content: "Tool-use requirement for the current turn: the application requires a call to the search_help_center tool in your response to the user's latest message. Begin your response with the search_help_center tool call. Do not reply with text only." } ] }); console.log(response.content);csharpAnthropicClient client = new(); var parameters = new MessageCreateParams { Model = "claude-fable-5-1", MaxTokens = 16000, System = "You are a customer support assistant for an online electronics store.", Tools = [ new ToolUnion(new Tool() { Name = "search_help_center", Description = "Search the help center for policy and troubleshooting articles.", Strict = true, InputSchema = new InputSchema(new Dictionary<string, JsonElement> { ["properties"] = JsonSerializer.SerializeToElement(new Dictionary<string, object> { ["query"] = new { type = "string" }, }), ["required"] = JsonSerializer.SerializeToElement(new[] { "query" }), ["additionalProperties"] = JsonSerializer.SerializeToElement(false), }), }), ], Messages = [ new() { Role = Role.User, Content = "My headphones from order A1234 arrived yesterday." }, new() { Role = Role.Assistant, Content = "Thanks for confirming. How can I help with order A1234?" }, new() { Role = Role.User, Content = "I opened the box. Can I still return them?" }, // The application requires a help center lookup before any policy // answer. Appending the requirement as a system message leaves the // earlier turns unchanged. new() { Role = Role.System, Content = "Tool-use requirement for the current turn: the application requires a call to the search_help_center tool in your response to the user's latest message. Begin your response with the search_help_center tool call. Do not reply with text only." } ] }; var message = await client.Messages.Create(parameters); Console.WriteLine(message);goclient := anthropic.NewClient() response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{ Model: "claude-fable-5-1", MaxTokens: 16000, System: []anthropic.TextBlockParam{ {Text: "You are a customer support assistant for an online electronics store."}, }, Tools: []anthropic.ToolUnionParam{ {OfTool: &anthropic.ToolParam{ Name: "search_help_center", Description: anthropic.String("Search the help center for policy and troubleshooting articles."), Strict: anthropic.Bool(true), InputSchema: anthropic.ToolInputSchemaParam{ Properties: map[string]any{ "query": map[string]any{"type": "string"}, }, Required: []string{"query"}, ExtraFields: map[string]any{ "additionalProperties": false, }, }, }}, }, Messages: []anthropic.MessageParam{ anthropic.NewUserMessage(anthropic.NewTextBlock("My headphones from order A1234 arrived yesterday.")), anthropic.NewAssistantMessage(anthropic.NewTextBlock("Thanks for confirming. How can I help with order A1234?")), anthropic.NewUserMessage(anthropic.NewTextBlock("I opened the box. Can I still return them?")), // The application requires a help center lookup before any policy // answer. Appending the requirement as a system message leaves the // earlier turns unchanged. { Role: anthropic.MessageParamRoleSystem, Content: []anthropic.ContentBlockParamUnion{ anthropic.NewTextBlock("Tool-use requirement for the current turn: the application requires a call to the search_help_center tool in your response to the user's latest message. Begin your response with the search_help_center tool call. Do not reply with text only."), }, }, }, }) if err != nil { log.Fatal(err) } fmt.Println(response.RawJSON())javavoid main() { AnthropicClient client = AnthropicOkHttpClient.fromEnv(); MessageCreateParams params = MessageCreateParams.builder() .model("claude-fable-5-1") .maxTokens(16000L) .system("You are a customer support assistant for an online electronics store.") .addTool(Tool.builder() .name("search_help_center") .description("Search the help center for policy and troubleshooting articles.") .inputSchema(InputSchema.builder() .properties(JsonValue.from(Map.of("query", Map.of("type", "string")))) .putAdditionalProperty("required", JsonValue.from(List.of("query"))) .putAdditionalProperty("additionalProperties", JsonValue.from(false)) .build()) .strict(true) .build()) .addUserMessage("My headphones from order A1234 arrived yesterday.") .addAssistantMessage("Thanks for confirming. How can I help with order A1234?") .addUserMessage("I opened the box. Can I still return them?") // The application requires a help center lookup before any policy // answer. Appending the requirement as a system message leaves the // earlier turns unchanged. .addMessage(MessageParam.builder() .role(MessageParam.Role.SYSTEM) .content("Tool-use requirement for the current turn: the application requires a call to the search_help_center tool in your response to the user's latest message. Begin your response with the search_help_center tool call. Do not reply with text only.") .build()) .build(); Message response = client.messages().create(params); IO.println(response); }php$client = new Client(); $message = $client->messages->create( maxTokens: 16000, messages: [ ['role' => 'user', 'content' => 'My headphones from order A1234 arrived yesterday.'], ['role' => 'assistant', 'content' => 'Thanks for confirming. How can I help with order A1234?'], ['role' => 'user', 'content' => 'I opened the box. Can I still return them?'], // The application requires a help center lookup before any policy // answer. Appending the requirement as a system message leaves the // earlier turns unchanged. ['role' => 'system', 'content' => 'Tool-use requirement for the current turn: the application requires a call to the search_help_center tool in your response to the user\'s latest message. Begin your response with the search_help_center tool call. Do not reply with text only.'] ], model: 'claude-fable-5-1', system: 'You are a customer support assistant for an online electronics store.', tools: [ [ 'name' => 'search_help_center', 'description' => 'Search the help center for policy and troubleshooting articles.', 'strict' => true, 'input_schema' => [ 'type' => 'object', 'properties' => [ 'query' => ['type' => 'string'] ], 'required' => ['query'], 'additionalProperties' => false ] ] ], ); echo $message;rubyclient = Anthropic::Client.new message = client.messages.create( model: "claude-fable-5-1", max_tokens: 16000, system: "You are a customer support assistant for an online electronics store.", tools: [ \{ name: "search_help_center", description: "Search the help center for policy and troubleshooting articles.", strict: true, input_schema: \{ type: "object", properties: \{ query: \{ type: "string" \} \}, required: ["query"], additionalProperties: false \} \} ], messages: [ \{ role: "user", content: "My headphones from order A1234 arrived yesterday." \}, \{ role: "assistant", content: "Thanks for confirming. How can I help with order A1234?" \}, \{ role: "user", content: "I opened the box. Can I still return them?" \}, # The application requires a help center lookup before any policy # answer. Appending the requirement as a system message leaves the # earlier turns unchanged. \{ role: "system", content: "Tool-use requirement for the current turn: the application requires a call to the search_help_center tool in your response to the user's latest message. Begin your response with the search_help_center tool call. Do not reply with text only." \} ] ) puts messageKeep the
role: "system"message in the history on later requests, as with any other turn. Mid-conversation system messages need no beta header.tool_choice: \{"type": "none"\}still works for a turn that must not call tools.Thinking blocks are preserved only for the model that produced them, or a newer one: Every
thinkingblock records which model produced it. Claude Fable 5.1 reads its own blocks and those from Claude Mythos 5.1, Claude Opus 5, Claude Fable 5, Claude Mythos 5, and earlier Claude models. A conversation moving ontoclaude-fable-5-1from any of those keeps its earlier reasoning. The condition is one-way: apart from Claude Mythos 5.1, none of those models can read Claude Fable 5.1's blocks.A conversation that ran on Claude Fable 5.1 can land on an older model through a router switch, a client-side retry, or a classifier refusal fallback, including a server-side fallback. The API removes the blocks that model can't read before it sees them, the request succeeds, and you aren't billed for the dropped input tokens. The target model re-plans without that reasoning, which can raise cost and latency on the first turn after the switch. To see what was dropped, send the
thinking-binding-controls-2026-08-01beta header: responses then carry aninput_transformationsarray naming each dropped block withreason: "model_binding_mismatch". See Preserved thinking.Editing earlier turns invalidates thinking blocks: Each
thinkingblock from Claude Fable 5.1 is valid only against thesystemprompt,tools, and conversation history that preceded it. If Claude Code, claude.ai, Claude Managed Agents, or the Claude Agent SDK manages your conversation history, it already keeps that prefix intact. If your code builds themessagesarray itself, this item applies to you, and Preserved thinking is the full integration guide. Where the check is enforced, a request that sends the block back after any of those changed is rejected with a 400 error:textmessages.5.content.0: Invalid `signature` in `thinking` block. The block is bound to a different conversation. Remove the block, or set `thinking.block_binding.prefix_mismatch_behavior` to "drop_block". That setting requires the `thinking-binding-controls-2026-08-01` value in the `anthropic-beta` header.The API enforces the check for new accounts created on or after August 31, 2026. For accounts created earlier, the API records the mismatch but doesn't act on it unless the request sets
thinking.block_binding.prefix_mismatch_behavior, which opts into enforcement. Anthropic plans to enforce the check for every account on future models, so make your application compatible now: the same patterns keep the prompt cache warm, and you can test against the check from any account by sendingprefix_mismatch_behavior. If you ship a tool or framework that people run with their own API key, test that way before launch: your key is probably on an older account, and your users on new ones hit the check before you do. To see whether your own account is enforced by default, send a request that edits history without the beta header: a 400 that names the header means it is.The error is permanent for that request body: an automatic retry loop won't clear it. To continue without the invalidated reasoning instead of failing, strip the
thinkingblocks from the history and retry once, or send thethinking-binding-controls-2026-08-01beta header and setprefix_mismatch_behaviorto"drop_block"(the default is"error"). With"drop_block", the API drops the mismatched block and every thinking block after it in the conversation, and reports each withreason: "prefix_binding_mismatch"in the response'sinput_transformationsarray:bashcurl https://api.anthropic.com/v1/messages \ -H "x-api-key: $ANTHROPIC_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "anthropic-beta: thinking-binding-controls-2026-08-01" \ -H "content-type: application/json" \ -d '{ "model": "claude-fable-5-1", "max_tokens": 16000, "thinking": { "type": "adaptive", "block_binding": { "prefix_mismatch_behavior": "drop_block" } }, "messages": [ { "role": "user", "content": "What is the greatest common divisor of 1071 and 462?" } ] }'```bash CLI ant beta:messages create \ --beta thinking-binding-controls-2026-08-01 \ --transform '{content.#(type=="text")#.text,input_transformations}' \ --format yaml < request.yaml ``` ```yaml model: claude-fable-5-1 max_tokens: 16000 thinking: type: adaptive block_binding: prefix_mismatch_behavior: drop_block messages: - role: user content: What is the greatest common divisor of 1071 and 462? ```pythonclient = anthropic.Anthropic() response = client.beta.messages.create( model="claude-fable-5-1", max_tokens=16000, thinking={ "type": "adaptive", "block_binding": {"prefix_mismatch_behavior": "drop_block"}, }, messages=[ { "role": "user", "content": "What is the greatest common divisor of 1071 and 462?", } ], betas=["thinking-binding-controls-2026-08-01"], ) for block in response.content: if block.type == "text": print(block.text) print(f"Input transformations: {len(response.input_transformations or [])}")typescriptconst client = new Anthropic(); const response = await client.beta.messages.create({ model: "claude-fable-5-1", max_tokens: 16000, thinking: { type: "adaptive", block_binding: { prefix_mismatch_behavior: "drop_block" } }, messages: [ { role: "user", content: "What is the greatest common divisor of 1071 and 462?" } ], betas: ["thinking-binding-controls-2026-08-01"] }); for (const block of response.content) { if (block.type === "text") { console.log(block.text); } } console.log(`Input transformations: ${response.input_transformations?.length ?? 0\}`);csharpusing Anthropic.Models.Beta; using Anthropic.Models.Beta.Messages; AnthropicClient client = new(); var response = await client.Beta.Messages.Create( new() \{ Model = "claude-fable-5-1", MaxTokens = 16000, Thinking = new BetaThinkingConfigAdaptive \{ BlockBinding = new() \{ PrefixMismatchBehavior = BetaThinkingPrefixMismatchBehavior.DropBlock, \}, \}, Messages = [ new() \{ Role = Role.User, Content = "What is the greatest common divisor of 1071 and 462?", \}, ], Betas = [AnthropicBeta.ThinkingBindingControls2026_08_01], \} ); foreach (var block in response.Content) \{ if (block.TryPickText(out var textBlock)) \{ Console.WriteLine(textBlock.Text); \} \} Console.WriteLine($"Input transformations: {response.InputTransformations?.Count ?? 0}");goclient := anthropic.NewClient() response, err := client.Beta.Messages.New(context.TODO(), anthropic.BetaMessageNewParams{ Model: "claude-fable-5-1", MaxTokens: 16000, Thinking: anthropic.BetaThinkingConfigParamUnion{ OfAdaptive: &anthropic.BetaThinkingConfigAdaptiveParam{ BlockBinding: anthropic.BetaThinkingBlockBindingParam{ PrefixMismatchBehavior: anthropic.BetaThinkingPrefixMismatchBehaviorDropBlock, }, }, }, Messages: []anthropic.BetaMessageParam{ anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("What is the greatest common divisor of 1071 and 462?")), }, Betas: []anthropic.AnthropicBeta{anthropic.AnthropicBetaThinkingBindingControls2026_08_01}, }) if err != nil { log.Fatal(err) } for _, block := range response.Content { if textBlock, ok := block.AsAny().(anthropic.BetaTextBlock); ok { fmt.Println(textBlock.Text) } } fmt.Printf("Input transformations: %d\n", len(response.InputTransformations))javaimport com.anthropic.models.beta.AnthropicBeta; import com.anthropic.models.beta.messages.BetaMessage; import com.anthropic.models.beta.messages.BetaThinkingBlockBinding; import com.anthropic.models.beta.messages.BetaThinkingConfigAdaptive; import com.anthropic.models.beta.messages.BetaThinkingPrefixMismatchBehavior; import com.anthropic.models.beta.messages.MessageCreateParams; void main() { AnthropicClient client = AnthropicOkHttpClient.fromEnv(); MessageCreateParams params = MessageCreateParams.builder() .model("claude-fable-5-1") .maxTokens(16000L) .addBeta(AnthropicBeta.THINKING_BINDING_CONTROLS_2026_08_01) .thinking(BetaThinkingConfigAdaptive.builder() .blockBinding(BetaThinkingBlockBinding.builder() .prefixMismatchBehavior(BetaThinkingPrefixMismatchBehavior.DROP_BLOCK) .build()) .build()) .addUserMessage("What is the greatest common divisor of 1071 and 462?") .build(); BetaMessage response = client.beta().messages().create(params); response.content().stream() .flatMap(block -> block.text().stream()) .forEach(textBlock -> IO.println(textBlock.text())); IO.println("Input transformations: " + response.inputTransformations().map(List::size).orElse(0)); }phpuse Anthropic\Beta\AnthropicBeta; use Anthropic\Beta\Messages\BetaThinkingBlockBinding; use Anthropic\Beta\Messages\BetaThinkingConfigAdaptive; use Anthropic\Beta\Messages\BetaThinkingPrefixMismatchBehavior; use Anthropic\Client; $client = new Client(); $response = $client->beta->messages->create( model: 'claude-fable-5-1', maxTokens: 16000, thinking: BetaThinkingConfigAdaptive::with( blockBinding: BetaThinkingBlockBinding::with( prefixMismatchBehavior: BetaThinkingPrefixMismatchBehavior::DROP_BLOCK, ), ), messages: [ ['role' => 'user', 'content' => 'What is the greatest common divisor of 1071 and 462?'], ], betas: [AnthropicBeta::THINKING_BINDING_CONTROLS_2026_08_01], ); foreach ($response->content as $block) \{ if ($block->type === 'text') { echo $block->text, PHP_EOL; \} \} echo 'Input transformations: ', count($response->inputTransformations ?? []), PHP_EOL;rubyclient = Anthropic::Client.new response = client.beta.messages.create( model: "claude-fable-5-1", max_tokens: 16_000, thinking: { type: "adaptive", block_binding: {prefix_mismatch_behavior: "drop_block"} }, messages: [ {role: "user", content: "What is the greatest common divisor of 1071 and 462?"} ], betas: [Anthropic::AnthropicBeta::THINKING_BINDING_CONTROLS_2026_08_01] ) response.content.each do |block| puts block.text if block.type == :text end puts "Input transformations: #{response.input_transformations&.length || 0}"The token counting endpoint runs the same check. See Controls for blocks that aren't preserved (beta) for the response shape and streaming placement.
Patterns that invalidate later thinking blocks, and what to do instead:
- Editing, reordering, or removing earlier turns. This includes deleting old tool results, snipping turns out of the middle of the transcript, and client-side compaction that keeps recent turns and their thinking blocks verbatim behind a summary (including background compaction that swaps its summary in a few turns later). Instead, use server-side compaction or context editing (tool result clearing for old tool results), or one of the client-side compaction shapes in Trim context on the server.
- Injecting content you don't persist, for example a per-turn reminder appended after the
tool_resultblocks and removed on the next request. Instead, send the reminder as a turn-scoped system message and leave it in the history. - Rebuilding the top-level
systemprompt or thetoolsarray between requests in the same conversation, for example to update the current date or to add or remove a tool. Instead, append a mid-conversation system message that carries the new instruction ("The current date is 2026-09-14.") ortool_additionandtool_removalblocks. - An image or document URL that serves different bytes on a later request. The check covers the bytes, not the URL string, so a rotating signed URL for the same file is fine. For content you reference across turns, upload it once with the Files API and send the
file_id, or send base64.
Each replacement also keeps earlier turns byte-identical and preserves the prompt cache hits that editing the history,
systemprompt, ortoolsarray would lose.Patterns that keep working:
- Append-only histories: adding turns and passing earlier turns back exactly as sent and received, including appended
role: "system"messages. - Removing thinking blocks from earlier assistant turns, oldest first.
- Changing
effort,max_tokens, or any other request parameter outsidesystem,tools, andmessages, and adding or movingcache_controlmarkers. - Server-side compaction and context editing, including thinking block clearing. They don't count as edits, because the check compares the conversation as you sent it.
To check an existing integration:
- Capture the exact request bodies it sends over a few normal turns, including a compaction or a tool change if your product has them. For each pair of consecutive requests, compare the
systemprompt, thetoolsarray, and the shared prefix ofmessages. They should be byte-identical up to the newly appended turns. - Run a normal multi-turn session against
claude-fable-5-1with thethinking-binding-controls-2026-08-01beta header andprefix_mismatch_behavior: "drop_block", and loginput_transformationson every response. An empty array on every turn means the history is intact. An entry withreason: "prefix_binding_mismatch"means something before the block atpathchanged since the previous request. An entry withreason: "model_binding_mismatch"means the conversation switched models, which isn't a bug in your code. This works from any account, because setting the field opts the request into enforcement. In CI, set"error"instead so an edit fails the run. - Choose a production setting. Leave the default
"error"if a prefix mismatch can only mean a bug in your code, or set"drop_block"to drop the affected blocks instead of failing, and monitor the 400s or theinput_transformationsentries either way.
Dropping thinking blocks once, at a compaction boundary for example, has little effect. An integration that invalidates prior thinking on every request restarts the prompt cache each time, which can raise cost per task (see Keep the conversation history append-only).
Behavior changes
Fewer parallel tool calls in long agent loops: In long-running loops where the next independent reads are only implied by the task (custom coding agents, bash-and-editor harnesses, computer use), Claude Fable 5.1 may issue one tool call per turn. Each extra turn costs tokens, a round trip, and wall-clock time. Append a one-sentence batching instruction after each user message as a turn-scoped system message (
clear_at: "next_user_message", beta), or, without the beta, in a text block after thetool_resultblocks, and leave the earlier copies in the history on later requests. See Batch independent tool calls in agent loops.Fewer progress messages between tool calls: Claude Fable 5.1 writes fewer status updates during long tool sequences than Claude Fable 5, and its agentic coding summaries are shorter. If your interface renders those updates, set
thinking.displayto"updates"(beta) or"summarized"and prompt for them explicitly. See Progress updates between tool calls and Ask for user-facing progress updates.Fewer search and retrieval calls at low effort: At
loweffort Claude Fable 5.1 answers from memory more often than Claude Fable 5 instead of calling a search or retrieval tool. If your product relies on retrieval at low effort, raise effort for those requests or tell the model when to search. See Search triggering at low effort.
For the differences in prose density, chat formatting, quoting in summaries, and file edits, which don't affect API integration, see Changed from Claude Fable 5.
Recommended changes
These changes aren't required, but each one lowers cost or latency or removes a failure mode:
Change effort mid-conversation (beta): On Claude Fable 5,
output_config.effortis request-level, and changing it between requests drops cached prefixes from earlier turns. Onclaude-fable-5-1, arole: "system"message carrying onlyoutput_configraises effort for a hard step or lowers it for routine ones without invalidating the prompt cache:bash# Effort-only system message: the new level takes effect from the next user turn. curl https://api.anthropic.com/v1/messages \ -H "x-api-key: $ANTHROPIC_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "anthropic-beta: mid-conversation-output-config-2026-07-01" \ -H "content-type: application/json" \ -d '\{ "model": "claude-fable-5-1", "max_tokens": 4096, "output_config": \{"effort": "high"\}, "messages": [ \{"role": "user", "content": "Plan a migration from SQLite to PostgreSQL in three short steps."\}, \{"role": "assistant", "content": "1. Export the SQLite data. 2. Create the PostgreSQL schema. 3. Import the data and verify row counts."\}, \{"role": "system", "content": [], "output_config": \{"effort": "low"}}, \{"role": "user", "content": "Summarize the plan in one sentence."\} ] \}'```bash CLI ant beta:messages create \ --beta mid-conversation-output-config-2026-07-01 \ --transform 'content.#(type=="text").text' \ --raw-output < request.yaml ``` ```yaml model: claude-fable-5-1 max_tokens: 4096 output_config: effort: high messages: - role: user content: Plan a migration from SQLite to PostgreSQL in three short steps. - role: assistant content: "1. Export the SQLite data. 2. Create the PostgreSQL schema. 3. Import the data and verify row counts." # Effort-only system message: the new level takes effect from the next user turn. - role: system content: [] output_config: effort: low - role: user content: Summarize the plan in one sentence. ```pythonclient = anthropic.Anthropic() response = client.beta.messages.create( model="claude-fable-5-1", max_tokens=4096, output_config=\{"effort": "high"\}, messages=[ \{ "role": "user", "content": "Plan a migration from SQLite to PostgreSQL in three short steps.", \}, \{ "role": "assistant", "content": "1. Export the SQLite data. 2. Create the PostgreSQL schema. 3. Import the data and verify row counts.", \}, # Effort-only system message: the new level takes effect from the next user turn. \{"role": "system", "content": [], "output_config": \{"effort": "low"}}, \{"role": "user", "content": "Summarize the plan in one sentence."\}, ], betas=["mid-conversation-output-config-2026-07-01"], ) for block in response.content: if block.type == "text": print(block.text)typescriptconst client = new Anthropic(); const response = await client.beta.messages.create(\{ model: "claude-fable-5-1", max_tokens: 4096, output_config: \{ effort: "high" \}, messages: [ \{ role: "user", content: "Plan a migration from SQLite to PostgreSQL in three short steps." \}, \{ role: "assistant", content: "1. Export the SQLite data. 2. Create the PostgreSQL schema. 3. Import the data and verify row counts." \}, // Effort-only system message: the new level takes effect from the next user turn. \{ role: "system", content: [], output_config: \{ effort: "low" \} \}, \{ role: "user", content: "Summarize the plan in one sentence." \} ], betas: ["mid-conversation-output-config-2026-07-01"] \}); for (const block of response.content) \{ if (block.type === "text") \{ console.log(block.text); \} \}csharpusing Anthropic.Models.Beta; using Anthropic.Models.Beta.Messages; AnthropicClient client = new(); var response = await client.Beta.Messages.Create(new MessageCreateParams \{ Model = "claude-fable-5-1", MaxTokens = 4096, OutputConfig = new() \{ Effort = Effort.High \}, Messages = [ new() \{ Role = Role.User, Content = "Plan a migration from SQLite to PostgreSQL in three short steps." \}, new() \{ Role = Role.Assistant, Content = "1. Export the SQLite data. 2. Create the PostgreSQL schema. 3. Import the data and verify row counts." \}, // Effort-only system message: the new level takes effect from the next user turn. new() \{ Role = Role.System, Content = new([]), OutputConfig = new() \{ Effort = BetaSystemMessageOutputConfigEffort.Low \}, \}, new() \{ Role = Role.User, Content = "Summarize the plan in one sentence." \}, ], Betas = [AnthropicBeta.MidConversationOutputConfig2026_07_01], \}); foreach (var block in response.Content) \{ if (block.TryPickText(out var textBlock)) \{ Console.WriteLine(textBlock.Text); \} \}goclient := anthropic.NewClient() response, err := client.Beta.Messages.New(context.Background(), anthropic.BetaMessageNewParams\{ Model: "claude-fable-5-1", MaxTokens: 4096, OutputConfig: anthropic.BetaOutputConfigParam\{ Effort: anthropic.BetaOutputConfigEffortHigh, \}, Messages: []anthropic.BetaMessageParam\{ anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("Plan a migration from SQLite to PostgreSQL in three short steps.")), \{ Role: anthropic.BetaMessageParamRoleAssistant, Content: []anthropic.BetaContentBlockParamUnion\{anthropic.NewBetaTextBlock("1. Export the SQLite data. 2. Create the PostgreSQL schema. 3. Import the data and verify row counts.")\}, \}, // Effort-only system message: the new level takes effect from the next user turn. anthropic.NewBetaSystemMessage(anthropic.BetaSystemMessageOutputConfigParam\{ Effort: anthropic.BetaSystemMessageOutputConfigEffortLow, \}), anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("Summarize the plan in one sentence.")), \}, Betas: []anthropic.AnthropicBeta\{anthropic.AnthropicBetaMidConversationOutputConfig2026_07_01\}, \}) if err != nil \{ log.Fatal(err) \} for _, block := range response.Content \{ if textBlock, ok := block.AsAny().(anthropic.BetaTextBlock); ok \{ fmt.Println(textBlock.Text) \} \}javaimport com.anthropic.models.beta.AnthropicBeta; import com.anthropic.models.beta.messages.BetaMessage; import com.anthropic.models.beta.messages.BetaMessageParam; import com.anthropic.models.beta.messages.BetaOutputConfig; import com.anthropic.models.beta.messages.BetaSystemMessageOutputConfig; import com.anthropic.models.beta.messages.MessageCreateParams; void main() \{ AnthropicClient client = AnthropicOkHttpClient.fromEnv(); MessageCreateParams params = MessageCreateParams.builder() .model("claude-fable-5-1") .maxTokens(4096L) .addBeta(AnthropicBeta.MID_CONVERSATION_OUTPUT_CONFIG_2026_07_01) .outputConfig(BetaOutputConfig.builder() .effort(BetaOutputConfig.Effort.HIGH) .build()) .addUserMessage("Plan a migration from SQLite to PostgreSQL in three short steps.") .addAssistantMessage("1. Export the SQLite data. 2. Create the PostgreSQL schema. 3. Import the data and verify row counts.") // Effort-only system message: the new level takes effect from the next user turn. .addMessage(BetaMessageParam.builder() .role(BetaMessageParam.Role.SYSTEM) .contentOfBetaContentBlockParams(List.of()) .outputConfig(BetaSystemMessageOutputConfig.builder() .effort(BetaSystemMessageOutputConfig.Effort.LOW) .build()) .build()) .addUserMessage("Summarize the plan in one sentence.") .build(); BetaMessage response = client.beta().messages().create(params); response.content().stream() .flatMap(block -> block.text().stream()) .forEach(textBlock -> IO.println(textBlock.text())); \}phpuse Anthropic\Beta\AnthropicBeta; use Anthropic\Beta\Messages\BetaMessageParam; use Anthropic\Beta\Messages\BetaOutputConfig; use Anthropic\Beta\Messages\BetaSystemMessageOutputConfig; use Anthropic\Client; $client = new Client(); $response = $client->beta->messages->create( model: 'claude-fable-5-1', maxTokens: 4096, outputConfig: BetaOutputConfig::with(effort: 'high'), messages: [ BetaMessageParam::with(role: 'user', content: 'Plan a migration from SQLite to PostgreSQL in three short steps.'), BetaMessageParam::with(role: 'assistant', content: '1. Export the SQLite data. 2. Create the PostgreSQL schema. 3. Import the data and verify row counts.'), // Effort-only system message: the new level takes effect from the next user turn. BetaMessageParam::with( role: 'system', content: [], outputConfig: BetaSystemMessageOutputConfig::with(effort: 'low'), ), BetaMessageParam::with(role: 'user', content: 'Summarize the plan in one sentence.'), ], betas: [AnthropicBeta::MID_CONVERSATION_OUTPUT_CONFIG_2026_07_01], ); foreach ($response->content as $block) { if ($block->type === 'text') \{ echo $block->text, PHP_EOL; \} \}rubyclient = Anthropic::Client.new response = client.beta.messages.create( model: "claude-fable-5-1", max_tokens: 4096, output_config: \{effort: :high\}, messages: [ \{role: "user", content: "Plan a migration from SQLite to PostgreSQL in three short steps."\}, \{role: "assistant", content: "1. Export the SQLite data. 2. Create the PostgreSQL schema. 3. Import the data and verify row counts."\}, # Effort-only system message: the new level takes effect from the next user turn. \{role: "system", content: [], output_config: \{effort: :low}}, \{role: "user", content: "Summarize the plan in one sentence."\} ], betas: [Anthropic::AnthropicBeta::MID_CONVERSATION_OUTPUT_CONFIG_2026_07_01] ) response.content.each do |block| puts block.text if block.type == :text endThe value applies to the following user turn and every later turn until another
role: "system"message changes it. Only the named levels are accepted (low,medium,high,xhigh,max), and themid-conversation-output-config-2026-07-01beta header is required. See Per-message effort.Change instructions and tools with mid-conversation system messages: To change instructions or tools partway through a session, append a
role: "system"message, withtool_additionandtool_removalblocks for tool changes (beta headermid-conversation-tool-changes-2026-07-01, with the full tool set declared intoolsat session start). This preserves prompt cache hits on earlier turns and keeps the conversation history append-only. The same message replaces forcedtool_choicewhen a specific tool must run on the current turn (see Breaking changes). For a reminder that applies to one turn only, send it as a separate text-onlyrole: "system"message withclear_at: "next_user_message"(turn-scoped system messages, beta headermid-conversation-system-clear-at-2026-08-21) and leave it in the history: it stops rendering after the next user message and costs no tokens once cleared. A message that carriestool_additionortool_removalblocks can't be turn-scoped.Use
fallbacks: "default"for refusals: Keep handlingstop_reason: "refusal"and readingstop_details.categorybefore response content. To re-run refused requests on another model automatically, setfallbacks: "default"(beta,server-side-fallback-2026-07-01header)."default"retries a declined request on the model Anthropic recommends for that category. The permitted fallback targets for Claude Fable 5.1 are Claude Opus 4.8 (claude-opus-4-8) and Claude Opus 5 (claude-opus-5). An explicitfallbackslist may name either. The fallback model doesn't receive Claude Fable 5.1's thinking blocks. If you build the retry yourself, fallback credit applies on the same terms as Claude Fable 5. See Refusals and fallback.Start at
higheffort and sweep: The effort parameter default ishigh, and all five levels are supported. Keep the Claude Fable 5 guidance:highfor most work, andmediumas a cost control worth testing. Claude Fable 5.1's gains over Claude Fable 5 are largest atxhighandmax, but those levels also add thinking time and time-to-first-response, so step up to them for the most capability-sensitive tasks and where your evals show the gain. Run a fresh sweep on your own evals rather than carrying over a setting tuned for Claude Fable 5. See Recommended effort levels for Claude Fable 5.1.Trim context on the server, or compact in a shape that carries no stale thinking: If your code truncates or summarizes older turns on the client, the simplest fix is to move that work to server-side compaction or context editing. Neither counts as an edit, because the history check compares the conversation as you sent it, so nothing they remove invalidates later thinking blocks, and compaction's
instructionsparameter accepts your own summarization prompt. If you keep compaction on the client, pick one of three shapes:- Simple compaction (recommended): replace the whole history with one summary message plus the new user turn and replay nothing else. No thinking blocks are carried over, so nothing fails. Claude models are trained on long-horizon tasks with this scheme, and it performs comparably to more elaborate ones for most workloads.
- Keep-tail compaction: if you keep the most recent turns verbatim behind a summary, strip the
thinkingandredacted_thinkingblocks from those turns (text and tool calls can stay), or setprefix_mismatch_behavior: "drop_block". Their thinking was produced against the full history and fails behind the summary otherwise. - Background compaction: if you build the summary off the critical path and swap it in later, every turn produced in the meantime carries thinking that predates the swap. Send
"drop_block"on every request that still carries thinking blocks produced before the swap (or strip those blocks yourself;input_transformationson the first response after the swap lists exactly which ones), or compact synchronously.
Don't snip individual turns out of the middle of the transcript: that invalidates every later thinking block and no client-side shape avoids it. Use a mid-conversation system message for the instruction change you were making, or server-side context editing for selective removal. See Passing compaction blocks back.
Migration checklist
- Update the model name from
claude-fable-5toclaude-fable-5-1(orclaude-mythos-5toclaude-mythos-5-1). - Replace forced
tool_choice(\{type: "any"\}or\{type: "tool", ...\}). It returns a 400 error. Use\{type: "auto"\}plus an explicit instruction andstrict: truetools, or JSON outputs. Put the instruction in theuserturn, or in a mid-conversationrole: "system"message when your application requires the call. - Keep passing
thinkingblocks back unchanged on every turn, including empty ones. Claude Fable 5.1 reads blocks from Claude Opus 5, Claude Fable 5, Claude Mythos 5, and earlier models. Moving a conversation from Claude Fable 5.1 to an earlier model drops its blocks (Claude Mythos 5.1 reads them). - If your code builds the
messagesarray itself, check whether it edits earlier turns: run a session with thethinking-binding-controls-2026-08-01beta header andprefix_mismatch_behavior: "drop_block", loginput_transformations, and fix everyprefix_binding_mismatch.model_binding_mismatchentries after a model switch are expected. - Keep conversation history append-only: freeze
systemandtoolsat session start and move mid-session changes torole: "system"messages andtool_addition/tool_removalblocks, send per-turn reminders as turn-scoped system messages you never remove, trim context server-side or strip thinking blocks from any turns you carry across a client-side summary, and reference cross-turn files byfile_id. - Pick a production
prefix_mismatch_behavior("error"by default, or"drop_block") and monitor it. If you maintain a tool that others run with their own API key, test with the field set: new accounts are enforced by default even if yours isn't. - Review agent loops for one-tool-call-per-turn behavior and add the batching instruction.
- If your interface renders progress text between tool calls, set
thinking.displayto"updates"(beta) or"summarized"and prompt for updates. - If you change effort between requests, move the change to a per-message effort
role: "system"message (beta) to keep cache hits. - Handle
stop_reason: "refusal"and readstop_details.category. Considerfallbacks: "default"(beta). - Re-evaluate
effortwith a fresh sweep, starting athigh, and re-baseline cost and latency on your own workloads. The tokenizer is unchanged. Prompt cache reads cost a quarter of the Claude Fable 5 rate.