●2.1.269 — Prompt suggestions were being dropped for Japanese, Chinese, Thai and other languages written without spaces between words. That is now fixed●KB5124008 — After September's cumulative update, Windows 11 users report Cowork failing to mount any host folder at all. The VM still starts, which makes the cause hard to pin down●PUSH — Some cloud and Cowork sessions have git push rejected by the proxy before it reaches GitHub. Cloning still works, so it reads like a token permission problem when it is not●NEW — Adding more material made Projects answer thinner. Three questions we now use to decide which knowledge files stay●TOKENS — You cannot price a PDF before sending it: CountTokens does not accept document input. That leaves estimating from page count or extracting the text and counting that●CLEANUP — Before asking an agent to tidy up, separate the work that only needs reading from the work that needs writing. The order you hand over folders cannot be reconsidered afterwards●2.1.269 — Prompt suggestions were being dropped for Japanese, Chinese, Thai and other languages written without spaces between words. That is now fixed●KB5124008 — After September's cumulative update, Windows 11 users report Cowork failing to mount any host folder at all. The VM still starts, which makes the cause hard to pin down●PUSH — Some cloud and Cowork sessions have git push rejected by the proxy before it reaches GitHub. Cloning still works, so it reads like a token permission problem when it is not●NEW — Adding more material made Projects answer thinner. Three questions we now use to decide which knowledge files stay●TOKENS — You cannot price a PDF before sending it: CountTokens does not accept document input. That leaves estimating from page count or extracting the text and counting that●CLEANUP — Before asking an agent to tidy up, separate the work that only needs reading from the work that needs writing. The order you hand over folders cannot be reconsidered afterwards
Running Claude Sonnet 4.6 in Production While Scouting the Move to Sonnet 5
Claude Sonnet 4.6 shipped February 17, 2026 and is now legacy, with retirement no sooner than February 17, 2027. Real prices against Opus 4.6, the 1M context, thinking, Computer Use, and the five places a Sonnet 5 migration returns 400.
I was staring at the per-model breakdown on my billing page one night. The Sonnet 4.6 row was still the longest bar by far, and right beneath it sat a short new row for Sonnet 5, which I had only started testing. Two weeks had gone by without me deciding which way to lean, and I noticed it standing in front of that screen.
Claude Sonnet 4.6 shipped on February 17, 2026 and still runs without trouble. Its status in the docs, though, has changed to legacy, and the recommended replacement is Claude Sonnet 5. Retirement is stated as no sooner than February 17, 2027 (Claude Sonnet 4.6 — Claude Platform Docs). There is no reason to rush. There is also no reason to keep postponing the scouting trip.
What I'd rather say first is that the thing which clouds a migration decision isn't capability — it's how cost presents itself. Line up the list prices and Sonnet 5 looks cheaper. As we'll see later, its new tokenizer produces roughly 30% more tokens for the same text, so the bill doesn't fall the way the price sheet suggests.
Specs, the 1M context, where thinking stands today, how to wire Computer Use, and the spots that return 400 on Sonnet 5 — in the order I hit them.
Key Specifications and Performance Benchmarks
Context Window
Claude Sonnet 4.6 supports a 1,000,000-token (1M token) context window. To put this in perspective, that's approximately 750,000 words in English — equivalent to around 2,500 pages of text. This isn't just a headline number; it fundamentally changes how you can architect AI applications.
The 1M window was a beta at launch. The current docs describe it differently: Claude 4.6 and later models include the full 1M token context window at standard pricing, so a 900k-token request is billed at the same per-token rate as a 9k-token one (Pricing — Long context pricing). No beta header is required.
Computer Use Performance
Sonnet 4.6 sits at 72.5 on OSWorld-Verified for computer use. Anthropic's launch post describes the arc rather than a single jump: across sixteen months from Sonnet 3.5, OSWorld scores moved from the teens into the low 70s, and scores from Sonnet 4.5 onward were measured on OSWorld-Verified, an in-place upgrade of the original benchmark (Introducing Claude Sonnet 4.6). Worth remembering when you compare numbers across generations: a re-measurement sits in the middle of that line.
Read 72.5 the other way and it says more than one attempt in four still goes sideways. What I took from the number wasn't an arrival point but a design constraint — build as if the failing quarter is normal.
Extended Thinking
Sonnet 4.6 supports Extended Thinking, allowing the model to work through complex problems systematically before delivering its response. This dramatically improves accuracy on tasks involving multi-step reasoning, mathematical derivations, system design, and nuanced judgment calls.
Pricing and Rate Limits
Sonnet 4.6 holds the same pricing as Sonnet 4.5. Cache writes come in two flavours — five minutes and one hour — at different rates, which is the part people miss.
The Messages Batches API raises max_tokens to 300,000 through its extended-output beta. The standard ceiling is 128K tokens, so that gap is what you're buying for long-form generation and large structured extraction.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦Rebuild the Opus 4.6 versus Sonnet 4.6 cost comparison from real list prices, then decide where your own workload belongs
✦Get working code for the 1M context, thinking, Computer Use, and Prompt Caching that accounts for what legacy models still allow
✦Learn the five breaking changes that return 400 on Sonnet 5, and how to price a move once the tokenizer shift is included
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
Opus 4.6 vs. Sonnet 4.6 — A Cost-Effectiveness Decision Framework
I Had to Drop the "Opus Costs 5x" Assumption
I designed for a while with the feeling that Opus was in another price bracket entirely. Then I pulled up the official price list and read the row again: Opus 4.6 is $5 / $25 per MTok. Against Sonnet 4.6's $3 / $15, that's roughly 1.7x on both input and output. Not a 5x gap.
The $15 / $75 figure belongs to Opus 4.1 and earlier, which still sit on the price page carrying a retired note. Read the wrong row from memory and Opus drops out of consideration before you've evaluated it.
Prices attach to generations, not to model names. Since that afternoon I reopen the relevant row every time I swap a model, which takes about fifteen seconds and has already saved me one bad architecture decision.
Three Axes Instead of a Matrix
Staring at a full model table, everything looks reasonable and nothing gets decided. These are the three things I actually look at:
Axis
What you check
Where it points
Tolerance for error
Can a human recover from one wrong answer?
No → Opus 5 / Fable 5.1
Volume of repetition
Thousands of identically shaped calls per day?
Yes → Sonnet 5 or Haiku 4.5
Capacity to migrate
Can you absorb breaking changes this month?
No → hold on Sonnet 4.6
The third axis isn't about capability, and in practice it's the one that decides. With retirement stated as no sooner than February 17, 2027, holding Sonnet 4.6 through the current quarter is a design choice rather than procrastination.
Compare the Same Job, Not the Same Token
Take an application processing 1 million input and 1 million output tokens per day:
Model
Per day
Per month (30 days)
Opus 4.6 ($5 / $25)
$30
$900
Sonnet 4.6 ($3 / $15)
$18
$540
Sonnet 5 ($2 / $10, list price only)
$12
$360
Sonnet 5 (with roughly 30% more tokens)
~$15.6
~$468
Moving from Opus 4.6 to Sonnet 4.6 saves about 40%, not 80%. And the reason Sonnet 5 occupies two rows is the point of this section. The docs state plainly that Claude 4.7 and later models use a new tokenizer producing about 30% more tokens for the same text (Pricing). A 33% cut in list price lands as roughly a 13% cut on the same job.
For a while I budgeted straight off the price sheet and told myself I'd save a third. That didn't hold up. List prices are only comparable while the tokenizer stays in the same generation. Across generations, running your real prompts through token counting settles the question faster than any amount of arguing about it.
Prompt Caching still pulls repeated input down to a tenth of the input rate. Ordering is what makes or breaks it, and I'll come back to that later on.
1M Token Context Window — Practical Applications
A 1M token context window isn't just about sending bigger prompts. It's an architectural shift that enables entirely new categories of applications.
Use Case 1: Full Codebase Analysis
import anthropicclient = anthropic.Anthropic()def first_text(message) -> str: """Return the first text block of a response. content[0] is not guaranteed to be text. With thinking enabled the thinking blocks arrive first, and tool-use replies lead with tool_use. Selecting by type instead of by position is the whole function — and it is what keeps this code working when you swap in Sonnet 5. """ for block in message.content: if block.type == "text": return block.text return ""def analyze_codebase(file_paths: list[str]) -> str: """ Load an entire codebase into context for holistic analysis. Works well for repositories up to ~800K tokens (leave buffer). """ files_content = "" for path in file_paths: with open(path, "r") as f: files_content += f"\n\n=== {path} ===\n{f.read()}" message = client.messages.create( model="claude-sonnet-4-6", max_tokens=8192, messages=[ { "role": "user", "content": f"""Analyze this entire codebase and provide:1. Architectural issues and improvement opportunities2. Security vulnerabilities and risk assessment3. Performance bottlenecks4. Code quality observationsCodebase:{files_content}""" } ] ) return first_text(message)# Expected output: Comprehensive architecture report with specific file/line references
Use Case 2: Long-Running Conversation State
Previously, long conversations hit token limits and required complex summarization strategies. With 1M tokens, you can maintain hundreds of conversation turns without degradation:
import Anthropic from "@anthropic-ai/sdk";interface Message { role: "user" | "assistant"; content: string;}class PersistentSession { private history: Message[] = []; private client: Anthropic; constructor() { this.client = new Anthropic(); } async chat(userMessage: string): Promise<string> { this.history.push({ role: "user", content: userMessage }); const response = await this.client.messages.create({ model: "claude-sonnet-4-6", max_tokens: 4096, system: "You are a dedicated engineering assistant with full context of this project's development history.", messages: this.history, // Full history — no truncation needed up to ~900K tokens }); const textBlock = response.content.find((b) => b.type === "text"); const reply = textBlock ? textBlock.text : ""; this.history.push({ role: "assistant", content: reply }); return reply; } estimateTokensUsed(): number { return this.history.reduce((sum, m) => sum + m.content.length / 4, 0); }}
Use Case 3: Multi-Document Structured Extraction
def extract_from_reports(documents: list[str]) -> str: """ Extract structured data from multiple business reports in a single call. Much faster and cheaper than processing documents individually. """ combined = "\n\n".join([ f"=== Document {i+1} ===\n{doc}" for i, doc in enumerate(documents) ]) response = client.messages.create( model="claude-sonnet-4-6", max_tokens=16384, messages=[{ "role": "user", "content": f"""Extract structured data from all documents below.Return valid JSON with this schema for each document:{{ "documents": [ {{ "revenue": "string", "profit_margin": "string", "key_metrics": ["string"], "risk_factors": ["string"], "summary": "string" }} ]}}Documents:{combined}""" }] ) return first_text(response)# Expected output: Valid JSON with extracted fields from each document
Before handing that string to json.loads, check whether first_text came back empty. It saves a round of debugging a JSON parse error that has nothing to do with your JSON.
Where Thinking Stands Now — From Manual Budgets to Adaptive
Thinking lets the model work through a problem before committing to an answer. It costs latency and buys accuracy, and the trade is usually worth it on design questions.
The generational picture is worth settling here. The Sonnet 4.6 model page lists thinking as Adaptive (extended deprecated). Specifying budget_tokens by hand still works on 4.6, but it has moved off the recommended path — and on Sonnet 5 the same request returns a 400 error.
Sonnet 4.6 (legacy)
Sonnet 5 (current)
Default behaviour
No thinking field → no thinking
Adaptive thinking on by default
thinking: enabled + budget_tokens
Works (deprecated)
400 error
Controlling depth
budget_tokens
output_config.effort (default high)
Thinking display
Defaults to summarized
Defaults to omitted
Turning it off
Omit the field
thinking: disabled
Source: Migrating to Claude Sonnet 5. Row four is the quiet one. If you render the reasoning summary in a UI on 4.6, swapping the model name alone leaves that panel blank.
Basic Implementation on Sonnet 4.6
import anthropicclient = anthropic.Anthropic()def think_deeply(problem: str, budget_tokens: int = 10000) -> dict: """ Use Extended Thinking for problems that benefit from deep reasoning. budget_tokens: How many tokens the model can use for internal thinking. Range: 1,024 to 32,000. Higher = more thorough reasoning. """ response = client.messages.create( model="claude-sonnet-4-6", max_tokens=16000, # Must exceed budget_tokens + expected output thinking={ "type": "enabled", "budget_tokens": budget_tokens, }, messages=[{"role": "user", "content": problem}], ) result = {"thinking": None, "answer": None} for block in response.content: if block.type == "thinking": result["thinking"] = block.thinking # Internal reasoning trace elif block.type == "text": result["answer"] = block.text # Final response return result# Example: System architecture decision with trade-off analysisproblem = """We're migrating a Python Flask monolith (PostgreSQL, 100k daily users)to microservices. Target: 99.99% SLA, horizontal scaling, team-independent deploys.What's the optimal migration strategy, and which service should we extract first?Justify your reasoning with specific risk and benefit analysis."""result = think_deeply(problem, budget_tokens=15000)print("Reasoning trace:", result["thinking"][:300], "...")print("\nFinal answer:", result["answer"])# Expected output: Detailed migration strategy with prioritized service extraction plan and risk assessment
The same routine on Sonnet 5 drops budget_tokens in favour of effort. The explicit display is there because the default of omitted returns thinking blocks with an empty field.
def think_deeply_v5(problem: str, effort: str = "high") -> dict: """Sonnet 5 version. Depth comes from effort, not from a token budget.""" response = client.messages.create( model="claude-sonnet-5", max_tokens=16000, thinking={"type": "adaptive", "display": "summarized"}, output_config={"effort": effort}, # lower effort = shallower, faster messages=[{"role": "user", "content": problem}], # temperature / top_p / top_k are omitted: non-default values return 400 ) out = {"thinking": None, "answer": None} for block in response.content: if block.type == "thinking": out["thinking"] = block.thinking elif block.type == "text": out["answer"] = block.text return out# Expected output: same dict shape as think_deeply, with summarized thinking
max_tokens remains a hard ceiling on thinking plus response text. Workloads that ran without thinking on 4.6 can start truncating on 5 at the same limit.
When to Use Extended Thinking
High-value scenarios:
Mathematical proofs and algorithm optimization
Complex system design with multiple interdependent trade-offs
Legal and compliance reasoning where precision is critical
Multi-variable optimization (database schema normalization, API versioning strategy)
Skip Extended Thinking for:
Standard Q&A and information retrieval
Simple code completion and syntax fixes
Data format transformation
Latency-sensitive API endpoints (Extended Thinking adds processing time)
Tool versions have turned over in the past year, so here's the compatibility table first. Sonnet 4.6 goes up to computer_20251124; the computer_toolset_20260801 toolset and the browser use tool arrived with Sonnet 5 and are not available on 4.6. Existing computer_20251124 integrations keep working unchanged on both models (Migrating to Claude Sonnet 5).
Tool
Sonnet 4.6
Sonnet 5
computer_20251124
Supported
Supported (unchanged)
computer_toolset_20260801
Not supported
Supported (Claude API / Google Cloud)
Browser use
Not supported
Supported
The code below uses computer_20251124, which runs on either model.
Core Implementation
import anthropicimport base64client = anthropic.Anthropic()def execute_computer_task(task: str, screenshot_b64: str) -> dict: """ Given a screenshot and task description, return the actions Sonnet 4.6 recommends to accomplish the task. """ response = client.messages.create( model="claude-sonnet-4-6", max_tokens=4096, tools=[ { "type": "computer_20251124", "name": "computer", "display_width_px": 1920, "display_height_px": 1080, } ], messages=[ { "role": "user", "content": [ { "type": "image", "source": { "type": "base64", "media_type": "image/png", "data": screenshot_b64, }, }, { "type": "text", "text": f"Look at the current screen and complete this task: {task}" } ], } ], ) actions = [] for block in response.content: if block.type == "tool_use" and block.name == "computer": actions.append({ "action": block.input.get("action"), "coordinate": block.input.get("coordinate"), "text": block.input.get("text"), }) return { "actions": actions, "reasoning": next( (b.text for b in response.content if b.type == "text"), "" ), }# Expected output: {"actions": [{"action": "click", "coordinate": [960, 540]}], "reasoning": "..."}
Production Safety Patterns
1. Always run in sandboxed environments
Never connect Computer Use directly to production systems. Use Docker containers or virtual machines as intermediaries to limit the blast radius of unexpected actions.
2. Implement human-in-the-loop for high-risk actions
System prompts and reference documents that appear in every request are ideal candidates for caching. Once cached, re-reading them costs just $0.30 per 1M tokens — a 90% reduction from the standard $3.
import anthropicclient = anthropic.Anthropic()SYSTEM_CONTEXT = """You are the AI assistant for Acme Corp.[Include extensive company knowledge, guidelines, product docs here...]The more text here, the greater the caching savings per request."""def cached_query(user_message: str) -> str: response = client.messages.create( model="claude-sonnet-4-6", max_tokens=4096, system=[ { "type": "text", "text": SYSTEM_CONTEXT, "cache_control": {"type": "ephemeral"}, # Enable caching } ], messages=[{"role": "user", "content": user_message}], ) # Monitor cache efficiency usage = response.usage cached_tokens = getattr(usage, "cache_read_input_tokens", 0) new_tokens = getattr(usage, "cache_creation_input_tokens", 0) print(f"Input: {usage.input_tokens} | Cached reads: {cached_tokens} | New writes: {new_tokens}") return first_text(response)# First call: Cache write at $3.75/1M tokensfirst = cached_query("What are our Q1 priorities?")# Subsequent calls: Cache read at $0.30/1M tokens — 90% cheapersecond = cached_query("Summarize the product roadmap.")
Messages Batches API for Async Workloads
For processing tasks where real-time response isn't required, Batches API delivers an additional 50% cost reduction. With Sonnet 4.6's 300K max_tokens cap, you can generate substantial content in each batch request.
import anthropicimport timeclient = anthropic.Anthropic()def batch_generate(tasks: list[dict]) -> list[str]: """ Process multiple generation tasks asynchronously. Cost: 50% off standard pricing. Turnaround: within 24 hours. """ requests = [ { "custom_id": f"task-{i}", "params": { "model": "claude-sonnet-4-6", "max_tokens": 8192, "messages": [{"role": "user", "content": task["prompt"]}], }, } for i, task in enumerate(tasks) ] batch = client.messages.batches.create(requests=requests) print(f"Batch {batch.id} created with {len(requests)} requests") # Poll for completion while True: status = client.messages.batches.retrieve(batch.id) if status.processing_status == "ended": break print(f"Processing... {status.request_counts.processing} remaining") time.sleep(30) # Collect results return [ first_text(result.result.message) for result in client.messages.batches.results(batch.id) if result.result.type == "succeeded" ]# Example: Generate 100 product descriptions in one batchtasks = [ {"prompt": f"Write a compelling 150-word product description for: {product}"} for product in product_list]descriptions = batch_generate(tasks)
Deploying Sonnet 4.6 in production without observability is flying blind. Here's a practical monitoring layer that captures the metrics that actually matter.
Tracking Token Usage and Cache Efficiency
import anthropicfrom dataclasses import dataclass, fieldfrom datetime import datetimeimport jsonclient = anthropic.Anthropic()@dataclassclass RequestMetrics: timestamp: str input_tokens: int output_tokens: int cache_read_tokens: int cache_write_tokens: int latency_ms: float model: str cost_usd: float def to_dict(self): return { "timestamp": self.timestamp, "input_tokens": self.input_tokens, "output_tokens": self.output_tokens, "cache_read_tokens": self.cache_read_tokens, "cache_write_tokens": self.cache_write_tokens, "latency_ms": self.latency_ms, "model": self.model, "cost_usd": self.cost_usd, }def compute_cost(usage, model: str = "claude-sonnet-4-6") -> float: """Compute actual cost in USD based on token usage.""" # Sonnet 4.6 pricing per 1M tokens INPUT_RATE = 3.00 / 1_000_000 OUTPUT_RATE = 15.00 / 1_000_000 CACHE_READ_RATE = 0.30 / 1_000_000 CACHE_WRITE_RATE = 3.75 / 1_000_000 cache_read = getattr(usage, "cache_read_input_tokens", 0) cache_write = getattr(usage, "cache_creation_input_tokens", 0) standard_input = usage.input_tokens - cache_read - cache_write return ( standard_input * INPUT_RATE + usage.output_tokens * OUTPUT_RATE + cache_read * CACHE_READ_RATE + cache_write * CACHE_WRITE_RATE )metrics_log: list[RequestMetrics] = []def monitored_call(prompt: str, system: str = None) -> str: """Wrap any Sonnet 4.6 call with automatic metrics collection.""" import time kwargs = { "model": "claude-sonnet-4-6", "max_tokens": 4096, "messages": [{"role": "user", "content": prompt}], } if system: kwargs["system"] = [ { "type": "text", "text": system, "cache_control": {"type": "ephemeral"}, } ] start = time.time() response = client.messages.create(**kwargs) latency = (time.time() - start) * 1000 usage = response.usage metrics = RequestMetrics( timestamp=datetime.utcnow().isoformat(), input_tokens=usage.input_tokens, output_tokens=usage.output_tokens, cache_read_tokens=getattr(usage, "cache_read_input_tokens", 0), cache_write_tokens=getattr(usage, "cache_creation_input_tokens", 0), latency_ms=round(latency, 2), model="claude-sonnet-4-6", cost_usd=round(compute_cost(usage), 6), ) metrics_log.append(metrics) # Log to your observability stack (Datadog, Grafana, CloudWatch, etc.) print(json.dumps(metrics.to_dict())) return first_text(response)def print_usage_summary() -> None: """Print aggregated stats across all requests in this session.""" if not metrics_log: return total_cost = sum(m.cost_usd for m in metrics_log) total_input = sum(m.input_tokens for m in metrics_log) total_cache_reads = sum(m.cache_read_tokens for m in metrics_log) avg_latency = sum(m.latency_ms for m in metrics_log) / len(metrics_log) cache_hit_rate = ( total_cache_reads / total_input * 100 if total_input > 0 else 0 ) print(f"\n=== Session Summary ===") print(f"Requests: {len(metrics_log)}") print(f"Total cost: ${total_cost:.4f}") print(f"Avg latency: {avg_latency:.0f}ms") print(f"Cache hit rate:{cache_hit_rate:.1f}%") print(f"Total tokens: {total_input + sum(m.output_tokens for m in metrics_log):,}")
Key Metrics to Alert On
Set up alerts for these thresholds to catch issues before they affect users:
Cache hit rate drops below 60%: Indicates your system prompt isn't being cached correctly, and costs are rising
P95 latency exceeds 5s: Suggests the model may be processing an oversized context or Extended Thinking is active unexpectedly
Error rate (429/529) exceeds 1%: You're hitting rate limits; implement request queuing or upgrade your tier
Cost per request doubles: Usually means the context window is growing unchecked; audit your history management logic
Structuring Logs for Debugging
Log both the input context and the model's reasoning when diagnosing quality issues in production. A structured log entry gives you everything you need to reproduce a problem:
import hashlibdef debug_log(prompt: str, response_text: str, thinking: str = None) -> None: """Write a structured log entry for quality auditing.""" entry = { "request_hash": hashlib.sha256(prompt.encode()).hexdigest()[:8], "prompt_length": len(prompt), "response_length": len(response_text), "thinking_length": len(thinking) if thinking else 0, "has_thinking": thinking is not None, # Truncate for log size management "prompt_preview": prompt[:200], "response_preview": response_text[:200], } print(json.dumps(entry))
Integrating Sonnet 4.6 into Existing Applications
Migration from Sonnet 4.5
The API interface is identical. In most cases, updating the model string is sufficient:
# Beforeresponse = client.messages.create( model="claude-sonnet-4-5", ...)# After — no other changes required in the vast majority of casesresponse = client.messages.create( model="claude-sonnet-4-6", ...)
Run both models in parallel for a week or two before cutting over, logging responses from each and comparing quality on your own tasks. Note that this smoothness is specific to the 4.5 → 4.6 step. Going from 4.6 to Sonnet 5 is a different kind of move, and the next section lays out why.
The Five Places a Move to Sonnet 5 Returns 400
I assumed swapping the model name would cover it. It did not. The official guide flags two breaking changes; in the order I actually tripped over them, there are five.
Step
How it reads on 4.6
What happens on 5
Fix
Manual thinking
thinking: enabled + budget_tokens
400 error
Drop it; use effort for depth
Sampling
temperature / top_p / top_k
400 on non-default values
Remove and steer with prompting
Reading the reply
content[0].text
Thinking blocks arrive first
Select by type
Thinking display
Defaults to summarized
Defaults to omitted, field empty
Set display: "summarized"
Refusals
—
stop_reason: "refusal" returns 200
Branch on it in the success path
Row three is the one that fails silently. No exception, no 4xx — content[0].text either raises an AttributeError or hands back an empty string. In tool-use loops you also have to pass thinking blocks back complete and unmodified; edit them and you get a 400.
To size a migration before committing, this probe is enough. Feed it your real prompts and watch tokens rather than dollars.
Token count up, cost down — seeing both lines together is what lets you say whether the move pays off for your workload rather than in general. Output is harder to predict without running it, so I shadow both models for a day and reconcile usage before cutting over.
The material for this decision is your own usage, not the price sheet. Obvious once written down, and every month I got the order wrong, my estimate was off.
What the docs don't tell you — notes from running it in production
A few behaviors only surface once you've shipped. Here is what I noticed as an indie developer after moving a support agent for one of my own apps over to Sonnet 4.6.
"Can hold 1M tokens" and "should send 1M tokens" are different claims
The 1M context is genuinely powerful, but once input crosses roughly 200K tokens, time-to-first-token grows noticeably. In my own measurements, the same question reached its first token in about 1.2s with a 30K input, but stretched to about 4.8s when I padded the input to 450K.
So 1M is the ceiling you can fill, not the amount you should send every call. Rather than keeping the entire history verbatim, I settled on passing the most recent 20–30 turns plus a summarized long-term memory separately. Both latency and cost stayed predictable.
Prompt Caching effectiveness depends on ordering
The docs say "put the stable parts first," but in practice a single variable element slipped between the system prompt and the tool definitions invalidates the entire cache after it.
I had originally appended the user's timezone to the end of the system prompt, and my cache hit rate came in at less than half of what I expected. Moving every variable element into the messages array lifted the post-write hit rate from 0.41 to 0.88. "Never place a variable behind the cache boundary" is an undocumented principle that maps directly to your bill.
With Computer Use, the real design is how you absorb the failing third
72.5 on OSWorld-Verified also means it misses roughly one attempt in three. A demo runs clean; production quality is decided by the recovery path when it fails.
The approach that worked for me was inserting a verification step before each Computer Use action — having the model state the current screen state in one sentence. That let it correct course just before a misstep, and task completion improved markedly. Designing for failure mattered more than the headline accuracy number.
Common Errors and Fixes
Error 1: context_window_exceeded
anthropic.BadRequestError: 400 {
"error": {
"message": "prompt is too long: 1050000 tokens > 1000000 maximum"
}
}
Fix: Implement graceful history truncation.
def smart_truncate(history: list, max_tokens: int = 900_000) -> list: """Remove oldest turns when approaching the context limit.""" estimated = sum(len(m["content"]) // 4 for m in history) while estimated > max_tokens and len(history) > 2: history.pop(0) # Remove oldest user turn history.pop(0) # Remove oldest assistant turn estimated = sum(len(m["content"]) // 4 for m in history) return history
Error 2: overloaded_error (HTTP 529)
Fix: Exponential backoff with jitter.
import timeimport randomdef resilient_call(prompt: str, max_retries: int = 3) -> str: for attempt in range(max_retries): try: response = client.messages.create( model="claude-sonnet-4-6", max_tokens=4096, messages=[{"role": "user", "content": prompt}], ) return first_text(response) except anthropic.APIStatusError as e: if e.status_code == 529 and attempt < max_retries - 1: wait = (2 ** attempt) + random.uniform(0, 1) print(f"API overloaded. Retrying in {wait:.1f}s ({attempt+1}/{max_retries})") time.sleep(wait) else: raise
Sonnet 4.6 has gone legacy, which is not the same as gone. With more than a year of stated runway before retirement, I see little reason to rip everything out this month. What deserves urgency isn't the switch itself but knowing what breaks when you make it.
For a first step, push one real system prompt through compare_input_cost above. Once you can see how much the token count grows, the conversation shifts from "should we move soon" to "when do we move this particular job."
Then grep for content[0].text and count the hits. I found seven in my own code. Select by type instead, and when the day comes to change models, the only thing that breaks is your cost estimate.
Thank you for reading. I hope this makes your own scouting trip a little shorter.
Share
Thank You for Reading
Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.