---
title: "Moving to Claude Sonnet 5.5: the three things that break"
description: "Sonnet 5.5 is 30% faster than Sonnet 5 at the same per-token price. Swapping the model name is easy; three habits in your code are not."
canonical: "https://me.consonance.fyi/en/blog/building-with-claude-sonnet-5-5/"
lang: "en"
published: "2026-10-03"
---

# Moving to Claude Sonnet 5.5: the three things that break

> Sonnet 5.5 is 30% faster than Sonnet 5 at the same per-token price. Swapping the model name is easy; three habits in your code are not.

By Minh-Toan Ta · 3 October 2026 · 4 min read

Every model upgrade starts with the same small lie we tell ourselves: *it's just a string*. You open the config, replace one model name with another, deploy, and go make coffee. With Claude Sonnet 5.5 that lie holds for maybe ten minutes, which is exactly how long it takes for the first request to come back in a shape your code did not expect, or not come back at all.

The upgrade is worth it. According to Anthropic, Sonnet 5.5 is smarter and more efficient than Sonnet 5 and 30% faster, and the per-token price is unchanged. Because it usually needs fewer tokens for the same job, the announcement puts the saving at up to 30% for most work. So the question is not *whether* to move, but what to fix on the way. I wrote a short sheet about this on [Consonance](https://consonance.fyi/en/mode-d-emploi/building-with-claude-sonnet-5-5); this is the longer version, with the code.

## The idea in one sentence

Change the model name, then fix the three places where Sonnet 5.5 stops tolerating old habits: reading the first block, turning thinking off, and forcing a tool.

```
model="claude-sonnet-5-5"
```

_Three Sonnet 5 habits and what replaces them on Sonnet 5.5._

## 1. Your code reads `content[0].text`

This is the quiet one, because it does not raise an API error. Sonnet 5.5 thinks by default: a request with no `thinking` field runs with adaptive thinking. That means the response can begin with a `thinking` block, and the line that grabs the text of the first block either crashes or hands you the wrong thing.

The fix is to stop assuming position and read each block by its type. This is the request Anthropic gives as working on Sonnet 5.5 as written:

```python
import anthropic

client = anthropic.Anthropic()

response = client.messages.create(
    model="claude-sonnet-5-5",
    max_tokens=4096,
    messages=[
        {
            "role": "user",
            "content": "Analyze the trade-offs between microservices and monolithic architectures",
        }
    ],
    output_config={"effort": "medium"},
)

for block in response.content:
    if block.type == "text":
        print(block.text)
```

The whole lesson is in that last loop, and specifically in one line:

```
if block.type == "text":
```

Two related habits from the migration guide are worth adopting at the same time. In tool-use loops, pass `thinking` blocks back unchanged, empty ones included. And revisit `max_tokens`: it covers thinking plus text, and thinking tokens are billed as output.

> **The trap**
>
> Reading `content[0].text`. Nothing fails at the request level, so this one survives a quick smoke test and breaks later. Read every block by its type.

## 2. You sent `thinking={"type": "disabled"}`

If you ran Sonnet 5 with thinking off, that exact setting now returns a 400 `invalid_request_error`. The replacement is a new value, `between_tools`. With it, the model does not think before responding; thinking only happens between tool calls, and Anthropic says total response time is the same or faster. Here is the before and after from the announcement:

```python
# Before: Claude Sonnet 5
client.messages.create(
    model="claude-sonnet-5",
    max_tokens=16000,
    thinking={"type": "disabled"},
    output_config={"effort": "xhigh"},
    messages=[{"role": "user", "content": "..."}],
)

# After: Claude Sonnet 5.5
client.messages.create(
    model="claude-sonnet-5-5",
    max_tokens=16000,
    thinking={"type": "between_tools"},
    output_config={"effort": "high"},
    messages=[{"role": "user", "content": "..."}],
)
```

Look closely: the effort also drops from `xhigh` to `high`. That is not cosmetic. `between_tools` comes with a few rules:

- It works at `low`, `medium` and `high` effort. At `xhigh` or `max` it returns a 400 error; to run there, use adaptive thinking.
- It takes no other field. Sending `display`, `budget_tokens` or `block_binding` alongside it returns a 400 error.
- Effort cannot change mid-conversation. If you want a different level per turn, use adaptive thinking.
- The short progress updates the model writes between tool calls still arrive as `thinking` blocks. Which brings us straight back to section 1.
- It needs no beta header, but if your SDK version does not define `between_tools`, update it.

One last nuance: for requests without tools that still need a few steps of working out, the announcement recommends adaptive thinking rather than `between_tools`.

## 3. You forced a tool with `tool_choice`

A `tool_choice` of type `any` or `tool` is rejected with a 400 error, including on the token counting endpoint. The message is explicit:

```text
tool_choice: type "tool" and "any" are not supported for this model.
```

The replacement has three parts. Send `tool_choice={"type": "auto"}`. Mark the tool `"strict": True` so its input matches the schema. And because `auto` means the model is free to answer without calling the tool, say in the prompt when to use it:

```python
weather_tool = {
    "name": "get_weather",
    "description": "Get the current weather in a given location",
    "input_schema": {
        "type": "object",
        "properties": {"location": {"type": "string"}},
        "required": ["location"],
        "additionalProperties": False,
    },
    "strict": True,
}

client.messages.create(
    model="claude-sonnet-5-5",
    max_tokens=1024,
    tools=[weather_tool],
    tool_choice={"type": "auto"},  # was {"type": "tool", "name": "get_weather"}
    messages=[
        {"role": "user", "content": "What's the weather in Paris? Use the get_weather tool."}
    ],
)
```

Strict tool use needs `additionalProperties: false` on every object in the schema, not only at the top level. That is the detail that tends to bite on nested inputs.

## Let Claude Code do the boring part

On a real project, these patterns are scattered across many files. Claude Code can run the migration for you through the bundled Claude API skill, which applies the model ID swap and the breaking parameter changes across the code base:

```
/claude-api migrate this project to claude-sonnet-5-5
```

Even then, a few things are judgment calls rather than search-and-replace:

- **Effort levels are recalibrated.** A level does not produce the same amount of thinking as on Sonnet 5, so your old setting does not carry over. Re-run your effort tests. The default is `high` on the Claude API and `medium` in Claude Code.
- **Large images cost more.** A 2000×1500 image costs about 2.5 times as many tokens as on Sonnet 4.6. If you do not need the detail, downscale before sending.
- **The full list is longer.** The guide counts five breaking changes and one change to the response shape. Beyond the ones above, check append-only conversations, computer use and the advisor tool if you use them.

> **Sonnet or Opus?**
>
> Sonnet 5.5 fits well-scoped work with a clear spec and a way to check the result: a bug to fix, a feature to iterate on, a few slides. For long, complex work that needs careful judgment, start with Opus 5.5.

## Wrapping up

The model name is the easy part. The real migration is three small changes in how your code talks to the model: read blocks by type, replace `disabled` with `between_tools`, and swap a forced tool for `auto` plus a strict schema and a clear prompt. Do those, re-run your effort tests, and you get a faster model at the same per-token price without a surprise 400 in production.

## Sources

- [Consonance · Moving to Claude Sonnet 5.5, in three coding situations](https://consonance.fyi/en/mode-d-emploi/building-with-claude-sonnet-5-5)
- [claude.dev · Building with Claude Sonnet 5.5](https://claude.dev/blog/building-with-claude-sonnet-5-5/)
- [Claude Docs · Claude Sonnet 5.5 migration guide](https://platform.claude.com/docs/en/models/sonnet-5-5/migration-guide)

---

- HTML: https://me.consonance.fyi/en/blog/building-with-claude-sonnet-5-5/
- [Français](https://me.consonance.fyi/blog/building-with-claude-sonnet-5-5.md) · [Tiếng Việt](https://me.consonance.fyi/vi/blog/building-with-claude-sonnet-5-5.md)
- MCP: https://mcp.consonance.fyi
- Contact: tmtoan2601@gmail.com · https://www.linkedin.com/in/minhtoanta/
