Fix Sonnet 5.5 API 400 errors after upgrading

Migrate to Sonnet 5.5 with a checklist for disabled thinking, forced tool use, conversation history and computer-use changes, plus a minimal request body.

Art-line illustration of key with the title Sonnet 5.5 API Migration.

Changing only the model ID can break a working Sonnet 5 integration. Sonnet 5.5 changes accepted thinking settings, forced tool use, thinking-history handling and some tool compatibility. If your client starts returning HTTP 400 after the upgrade, inspect the error body and the outgoing request before changing authentication or retrying the same payload.

This guide follows Anthropic’s Sonnet 5.5 migration guide and change documentation, checked September 29, 2026. The examples are documentation-based request shapes, not a claim that Ofox has reproduced every error against a live API. A 401, 429 or provider-specific 404 needs a different investigation.

Identify the incompatible field

Existing configurationSonnet 5.5 changeFirst action
thinking.type: disabledRejectedUse between_tools at high effort or below
Manual enabled with budget_tokensRejectedUse supported adaptive thinking or between_tools
tool_choice.type: any or toolRejectedUse auto; validate tool selection in the application
Edited history plus replayed thinking blocksCan violate conversation bindingPreserve append-only history or follow the documented block-dropping flow
computer_20251124 on Claude API/Google CloudRejectedMigrate to the supported computer toolset and update the loop
Older advisor model pairingSome pairings rejectedCheck the supported advisor list

Do not apply the computer-use row to all providers. The same official page says Amazon Bedrock accepts the older computer_20251124 tool. Platform scope is part of the fix.

Replace disabled thinking carefully

For a minimal text request with no tools, this is a documentation-based body for POST /v1/messages on the native Claude API:

{
  "model": "claude-sonnet-5-5",
  "max_tokens": 1024,
  "thinking": {"type": "between_tools"},
  "output_config": {"effort": "high"},
  "messages": [{"role": "user", "content": "Return a one-sentence summary of this task: verify a CSV total."}]
}

The body alone is not a complete HTTP client. Supply the authentication and API-version headers required by the Messages API. Keep credentials in your own environment, outside copied examples and logs.

between_tools disables up-front thinking, but it is not a promise that every tool workflow has no thinking blocks. Progress notes between tools can still use that block type. It accepts low, medium and high effort, not xhigh or max, and does not accept extra fields such as display or budget_tokens. To use xhigh or max, use adaptive thinking. Do not combine incompatible settings and expect retries to resolve the validation error.

Replace forced tool calls without losing validation

Switching tool_choice to auto changes behavior: the model can choose whether to call a tool. Adding strict: true to a supported tool definition validates the shape of a tool input; it does not force the model to select that tool. Your application must still check whether the expected call happened. These schema options are platform-dependent: the migration guide says structured outputs, including strict tool use, are unavailable for Sonnet 5.5 on Amazon Bedrock.

For an extraction service, consider whether you need a tool call at all. Structured output can be the appropriate design when the result is data rather than an action. Test valid output, omitted required data, refusal and an unexpected natural-language response. Do not declare the migration complete merely because the request stops returning 400.

Preserve conversation history

Sonnet 5.5 binds its thinking blocks to the model and conversation. Editing an earlier system prompt, tool definition or message while replaying a later block can trigger a binding error. The official default enforcement applies to accounts created on or after August 31, 2026, 00:00 UTC on specified platforms; older accounts and explicit opt-in settings require separate checking.

The simplest design is append-only history. Keep returned blocks unchanged and use the documented mechanisms for mid-conversation changes. If you deliberately edit history, follow the migration guide’s handling of affected blocks and beta controls. Do not strip every thinking block from every request as a universal fix: that changes the conversation and can discard useful context.

Model switching has its own rules. A block that cannot be read by the target model can be dropped, which is different from an edited-prefix binding failure. Log the actual error or transformation metadata instead of assigning every problem the label “invalid signature.” For older cases, see the thinking signature troubleshooting guide.

Check successful responses too

Some regressions do not return an HTTP error. Longer progress notes between tool calls can arrive in thinking blocks whose text is omitted under the default adaptive display behavior. A UI that renders only text blocks can look silent while the request is otherwise valid. Check the documented thinking.display behavior for adaptive thinking or the supported between_tools mode.

Also distinguish a refusal from a transport failure. The documentation describes HTTP 200 with stop_reason: refusal and additional details. A successful HTTP status is not proof that the requested task completed. Handle the result explicitly rather than repeatedly submitting the same declined task.

Reproduce one failing request without your agent loop

Before changing the production integration, save the failing request with secrets and private input removed. Record the endpoint host, model ID, SDK version, HTTP status, error type, error message and request ID when present. Keep the original response locally; copying only “400 Bad Request” removes the information needed to distinguish incompatible thinking from an invalid message sequence. Disable your application’s automatic retry during this isolated diagnostic. An unchanged invalid request normally needs a configuration correction, not ten identical attempts.

Start a fresh conversation with the minimal JSON above, saved as request.json. This shell example calls the native Anthropic endpoint and consumes API usage if you execute it. It assumes an approved account and ANTHROPIC_API_KEY already set; do not paste a key into the command or publish the saved response. It is a diagnostic recipe, not a transcript of our successful model run.

curl --silent --show-error \
  --dump-header response.headers \
  --output response.json \
  --write-out 'HTTP %{http_code}\n' \
  https://api.anthropic.com/v1/messages \
  --header "x-api-key: $ANTHROPIC_API_KEY" \
  --header 'anthropic-version: 2023-06-01' \
  --header 'content-type: application/json' \
  --data-binary @request.json

There are two results to examine: the printed HTTP status and the JSON body. A shell exit code of zero means curl completed its transfer; without an HTTP failure option it does not mean the API accepted the request. If the minimal request succeeds, add your original system prompt, tools and history back one group at a time. The first addition that reproduces the failure gives a much narrower investigation than changing the model, SDK and tool definitions simultaneously.

If it still returns 400, compare the actual serialized JSON with the example. An SDK wrapper may reinsert thinking.type: disabled, a manual token budget or forced tool choice after your application code removed it. Inspect the outgoing body rather than only the configuration object. For a gateway, confirm its documented native-API compatibility before assuming it forwards these fields unchanged.

Make the request change and the parser change together

A useful before-and-after review covers behavior as well as valid JSON:

BeforeAfterAdditional acceptance condition
thinking: {"type":"disabled"}thinking: {"type":"between_tools"}No unsupported fields inside thinking; effort remains low, medium or high
tool_choice: {"type":"tool","name":"record_expense"}tool_choice: {"type":"auto"}Application detects no-call, one-call and unexpected-call responses
A parser reads only the first content blockA parser examines block typesText, thinking and tool calls are handled without executing unknown tools
HTTP 200 means successStatus plus stop_reason plus task checksRefusal, truncation and incomplete work never become successful business records

For example, a tool-driven expense application should not insert a record just because text says “saved.” It should accept only an allowlisted tool_use name, validate the input and obtain the necessary application authorization. When returning its result, preserve the assistant content and match the result to that tool-use ID. If there are multiple calls, pair each result explicitly; do not attach every result to whichever call happened to arrive last.

A missing call under auto is now an expected branch. Return a useful incomplete-task state to the user or request the missing information. Retrying with tool_choice: any brings back the incompatibility. Likewise, strict: true helps input-shape enforcement on supported platforms but does not prove that an amount, recipient or date is correct. Business validation remains necessary after schema validation.

Diagnose history separately from a new conversation

Suppose a conversation succeeded with system instruction A, then you edit A to B and replay the signed thinking that followed A. Under the documented binding rules, the prior thinking no longer corresponds to the conversation prefix. Increasing max_tokens will not repair that relationship. Reproduce the same task in a new conversation without replaying the old blocks. If the new conversation works, investigate your history mutation, not the token budget.

Keep the original transcript as an immutable record. Do not fabricate replacement signatures or copy thinking from another account. For an intentional history edit, implement the official migration flow for the affected blocks and supported controls. Do not turn a temporary diagnostic that starts a fresh conversation into a silent production policy that discards every user’s context. Test both a genuinely append-only conversation and the edit your application actually performs.

Model switching is another separate test. An incompatible block can be dropped with a 200 response, whereas a binding violation can fail the request. Your logs should distinguish “request failed” from “request succeeded with changed history.” This matters when comparing a resumed task with a fresh task: they may not have identical context even if their visible user messages match.

Use a response contract before rollout

For the minimal text example, acceptance means an HTTP success, a message body with usable text, an appropriate completion reason and a summary that matches the input. For a tool workflow, add correct tool name, schema, result pairing and final task completion. A max_tokens stop is a limit event; do not silently accept a half-written JSON object or partial instruction as final output. A refusal is its own result and should not enter the same automatic retry path as a transient network error.

Keep six small regression cases: a fresh text request, a valid tool action, an answer without a tool call, an append-only second turn, an intentionally changed history prefix and a response-parser fixture containing multiple block types. The last case can run locally using synthetic JSON. It tests your parser only, not Sonnet’s behavior. Run those fixtures before a controlled live sample, then compare the old and new application results on the same approved inputs.

Roll back if the new integration produces invalid business actions or loses required context even when its HTTP error rate improves. Keep the old request adapter and new adapter separately named until the rollout is accepted. The migration is complete when both the request and the business outcome satisfy their contracts, not merely when the server returns 200.

Validate before switching production traffic

Use the upgrade decision guide for the broader rollout checklist and the Claude Code setup guide for CLI selection. This article addresses native API changes; a third-party gateway may add its own translation layer and errors.

Frequently Asked Questions

Can I retain disabled thinking?
Not with that field value on Sonnet 5.5. The documented replacement is between_tools at high effort or below; adaptive thinking supports the higher effort levels.
Does strict tool use force a tool call?
No. Schema validation and selecting a tool are different requirements. Your application must handle a response that does not call the desired tool.
Does every 400 mean the model upgrade is responsible?
No. Read the precise error and isolate the changed field. Malformed messages, provider adaptation and other invalid parameters can also return 400.