"I turned on Extended Thinking but the response looks no different from before." I have heard this often, and I have fallen for the same trap many times while writing SDK code. After enough debugging, the causes resolve into seven distinct categories. The hard part is separating display issues from configuration issues from implementation bugs.
What makes Extended Thinking tricky is that "turning it on" is often not enough — and the failure modes look identical from the outside. This guide starts where every diagnosis should: confirming from the API response whether thinking actually ran.
First, Verify That Thinking Is Actually Running
When Extended Thinking is working, the Claude API returns a content array that includes one or more blocks of type: "thinking". If those blocks are missing, something has silently disabled thinking.
import anthropic
client = anthropic.Anthropic(api_key="YOUR_API_KEY")
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=8192,
thinking={"type": "enabled", "budget_tokens": 4096},
messages=[{"role": "user", "content": "What is the sum of all primes from 1 to 100?"}]
)
# Check 1: Are there thinking blocks?
thinking_blocks = [b for b in response.content if b.type == "thinking"]
text_blocks = [b for b in response.content if b.type == "text"]
print(f"thinking blocks: {len(thinking_blocks)}")
print(f"text blocks: {len(text_blocks)}")
print(f"thinking length: {sum(len(b.thinking) for b in thinking_blocks)} chars")
# Check 2: Did usage report thinking_tokens?
print(f"thinking tokens used: {response.usage.thinking_tokens if hasattr(response.usage, 'thinking_tokens') else 'N/A'}")If thinking_blocks is empty or thinking_tokens is zero, you are hitting one of the seven causes below.
Cause 1: The Model Does Not Support Extended Thinking
The first thing to check is whether the model you are calling supports Extended Thinking at all. Not every Claude model does — claude-haiku-3-5, for example, does not. As of April 2026 the supported models are:
| Model | Extended Thinking | Recommended budget_tokens |
|---|---|---|
| claude-opus-4-6 | Yes (strongly recommended) | 4096–32000 |
| claude-sonnet-4-6 | Yes | 2048–16000 |
| claude-haiku-4-5 | Yes (lightweight tasks) | 1024–8192 |
| claude-haiku-3-5 | No | — |
Sending the thinking parameter to an unsupported model does not raise an error — it is silently ignored. This is one of the most common reasons "thinking does not appear to be running."
Cause 2: budget_tokens Is Too Small
budget_tokens is the cap on tokens spent inside the thinking phase. When it is too small, Claude starts thinking but cuts off almost immediately, producing responses that feel identical to a non-thinking model. From experience, anything below 1024 is essentially meaningless. I recommend at least 2048, and 4096 or higher when the problem genuinely needs reasoning depth.
Also watch the relationship with max_tokens. max_tokens is the combined cap on thinking + text, so a configuration like max_tokens=4096, budget_tokens=4096 can leave zero tokens for the actual answer. The rule of thumb: max_tokens >= budget_tokens + expected_answer_length.
Cause 3: Claude.ai vs. API Behavioral Differences
When you toggle Extended Thinking in the Claude.ai chat UI, the model and budget being used internally are not exposed. Pro / Max plans appear to think for longer, while Free or even mid-session Pro behavior can shrink the budget at peak times.
If you see "the API thinks deeply but the chat barely thinks" — first reproduce on a fresh Pro session, then compare the same prompt via the API. I always cross-check chat behavior against the API when something feels off, because matching the two reveals which side has the actual problem.
Cause 4: Streaming Code That Drops Thinking Blocks
When you receive responses with stream=True, an SDK event loop that does not handle thinking blocks will silently throw them away. The blocks exist in the response, but never reach your screen.
# Common mistake: only consuming text blocks
with client.messages.stream(
model="claude-opus-4-6",
max_tokens=8192,
thinking={"type": "enabled", "budget_tokens": 4096},
messages=[{"role": "user", "content": "..."}]
) as stream:
for text in stream.text_stream: # thinking never arrives here
print(text, end="")
# Correct pattern: process events explicitly
with client.messages.stream(...) as stream:
for event in stream:
if event.type == "content_block_start":
if event.content_block.type == "thinking":
print("\n[Thinking starts]")
elif event.content_block.type == "text":
print("\n[Answer starts]")
elif event.type == "content_block_delta":
if event.delta.type == "thinking_delta":
print(event.delta.thinking, end="")
elif event.delta.type == "text_delta":
print(event.delta.text, end="")text_stream is convenient but discards thinking. To make Extended Thinking visible, you need to handle events explicitly.
Cause 5: Interleaved Thinking Disabled Alongside Tool Use
When combining tool calls with thinking, the default behavior thinks once at the very first response block only. If you want thinking to recur between successive tool calls, you must opt into the interleaved-thinking-2025-05-14 beta header.
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=16000,
thinking={"type": "enabled", "budget_tokens": 8000},
extra_headers={"anthropic-beta": "interleaved-thinking-2025-05-14"},
tools=[your_tools],
messages=[...]
)Without this header, you get "thinking before the first tool call, then ordinary responses afterward" — which silently kills the reasoning depth your agent workflow expected. I lost an entire day to this one when first building agentic systems.
Cause 6: Conflict with Prompt Caching
When prompt caching is enabled, thinking does not run over the cached portion of the context. This is by design: the cached prefix is treated as "already reasoned over."
If thinking suddenly feels shallower after your cache hit rate climbed, check response.usage.cache_read_input_tokens. When the cached portion dominates, thinking only runs on the freshly added input. If you need thinking on a section, you must keep that section outside the cache boundary.
Cause 7: Beta Feature Region Restrictions
When calling Claude through AWS Bedrock or GCP Vertex AI, beta feature support for Extended Thinking rolls out to regions on different timelines. I have seen multiple reports of "works on the Anthropic API directly, does not work via Bedrock" — and almost every time, it is a region × model-version combination problem.
The fastest disambiguation is to call the Anthropic API directly with the same model and parameters. If it works there but not via Bedrock, you either wait for the Bedrock model update or switch to a supported region.
Diagnostic Flow: The Order I Actually Use
Here is the checklist I run when this issue lands on my desk:
- Check the model name (rule out unsupported models like claude-haiku-3-5)
- Print the API response
contentarray to verify whether thinking blocks exist - Raise
budget_tokensto 8192 and rerun — if still empty, it is not a configuration issue - If
stream=True, switch fromtext_streamto event-loop handling - If using tools, add the
interleaved-thinking-2025-05-14header - Check
cache_read_input_tokens— when cached input dominates, thinking will not run - Confirm against the direct Anthropic API — if it works there but not via your gateway, it is a relay issue
This order resolves nearly every "Extended Thinking is not working" report I have encountered.
Bonus: When Thinking Works Too Well
The opposite problem — thinking that runs too long and inflates latency or cost — also happens. Cut budget_tokens in half first, and if you need finer control add the dynamic budget header extra_headers={"anthropic-beta": "thinking-budget-policy-2025-09-15"}.
Extended Thinking is powerful, but it requires tuning in both directions: turning it on enough to matter, and keeping it from running away. Keep this diagnostic flow nearby — next time the symptom appears, you will spend minutes instead of hours figuring out which of the seven causes is yours.