Read Thinking Blocks

Su Suriya Ravichandran Updated 10 Nov 2026
5 min read ·Lesson 153 of 200

What Comes Back When Claude Thinks

When thinking is enabled, a response contains more than text. Its content list holds thinking blocks before the text, and sometimes before the tool calls too. Each thinking block carries the reasoning Claude produced, along with a signature, a value that identifies the block and lets the API verify it later. Reading these blocks correctly is the difference between an application that shows users a clean answer and one that breaks the moment a tool is involved.

This lesson explains the structure of a thinking block, the two display settings that control what text it contains, how to separate thinking from answers in code, and why the blocks must be passed back unchanged. The rule about passing blocks back is the one that breaks most often, so it gets the most attention.


The Shape of a Thinking Block

A thinking block is an object with a type and two main fields:

{
    "type": "thinking",
    "thinking": "The user asks whether 1,001 is prime. 1,001 = 7 × 11 × 13...",
    "signature": "EqQBCgIYAhIM...",
}
FieldMeaning
typeAlways "thinking" for reasoning blocks
thinkingThe reasoning text, or a summary of it, depending on the display setting
signatureAn opaque value that authenticates the block, and must be preserved

The signature is not for display or for your logic. Its only purpose is to let the API confirm that a thinking block is genuine when it is returned in a later request. A block whose signature is missing or altered cannot be used in a continuation, and this is why the rule about passing blocks back is strict.


Two Display Settings

Thinking text can be returned in more than one form, controlled by the display setting inside the thinking object:

DisplayWhat the thinking block containsWhen to use it
summarizedA summary of the reasoningShowing reasoning to people, or logging it for review
omittedAn empty thinking field, with only the signatureKeeping the signature for continuations, without exposing the reasoning text

The omitted form keeps everything the API needs, and drops the text. An application that never shows reasoning, but must continue tool conversations, can use it to reduce the amount of text it stores and transmits. The documentation describes the exact default for each model, and that default should be confirmed rather than assumed.

response = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=4096,
    thinking={"type": "adaptive", "display": "summarized"},
    messages=[{"role": "user", "content": "Is 1,001 prime?"}],
)

Separating Reasoning from Answers

Because thinking blocks come first, the simplest mistake is to take the first block and assume it is text. The correct approach selects blocks by type, as Lesson 42 recommended. The helper below separates a content list into its parts, and reports how many thinking blocks it found:

"""thinking_blocks.py: separate reasoning, text, and tool calls from a response's content."""


def split_content(content: list[dict]) -> dict:
    parts = {"thinking": [], "text": [], "tool_use": [], "signatures": []}
    for block in content:
        kind = block.get("type")
        if kind == "thinking":
            parts["thinking"].append(block.get("thinking", ""))
            parts["signatures"].append(block.get("signature"))
        elif kind == "text":
            parts["text"].append(block.get("text", ""))
        elif kind == "tool_use":
            parts["tool_use"].append(block.get("name"))
    return parts


if __name__ == "__main__":
    content = [
        {"type": "thinking", "thinking": "1,001 = 7 x 11 x 13, so it is not prime.",
         "signature": "sig-a"},
        {"type": "text", "text": "No, 1,001 is not prime."},
    ]
    parts = split_content(content)
    print("thinking blocks:", len(parts["thinking"]))
    print("answer:", "".join(parts["text"]))
    print("signatures kept:", parts["signatures"])

Output:

thinking blocks: 1
answer: No, 1,001 is not prime.
signatures kept: ['sig-a']

How It Works

  1. Blocks are selected by type, not by position. The thinking block is first in the list, but the code would find the text correctly even if the order changed.
  2. The reasoning and the signature are kept separately. The text can be logged or displayed, and the signature is retained for any later continuation.
  3. The answer is assembled from text blocks only, which is what the user should see. The reasoning is not mixed into the reply.
  4. The function returns a dictionary of lists, so a response with several thinking blocks, which can occur around tool calls, is handled without special cases.

Passing Blocks Back Unchanged

The rule that matters most is this: when a conversation continues after a tool call, the assistant's thinking blocks must be sent back exactly as they were received. The documentation is explicit that thinking blocks carry the reasoning behind tool calls, and that they should be returned unmodified, with their signatures intact.

messages.append({"role": "assistant", "content": response.content})   # The whole content, untouched
messages.append({"role": "user", "content": [tool_result_block]})

The mistake to avoid is rebuilding the assistant message from its text alone, or editing the thinking text, or dropping the blocks to save tokens. Each of those changes the content the API verified, and the continuation fails or behaves unpredictably. The safe pattern appends the response's content as received, which is the same practice Lesson 88 introduced for tool loops.

The documentation also notes that thinking is interleaved with tool use automatically on adaptive thinking, so a single assistant turn can contain thinking blocks around several tool calls. Passing the full content back preserves that structure.


Thinking and Caching

Thinking blocks interact with prompt caching in two ways. They cannot be marked with a cache breakpoint directly, but they can be cached as part of earlier assistant turns in a conversation. When a cached thinking block is read back, it counts as input tokens, so it contributes to cost like any other input. Changing the thinking configuration between requests invalidates cached message prefixes, as Lesson 127 described, so a conversation should keep its thinking settings steady.


Keeping Reasoning Private

Reasoning text can contain material that should not reach every user, such as internal notes, sensitive data that Claude considered, or wording that is confusing out of context. Displaying the summarized reasoning is a product decision, and the safest default is to show only the answer. When reasoning is shown, the application should label it as reasoning and present it with the same care as any other model output, and it should be logged according to the policy in Lesson 147.


Summary

A thinking block carries reasoning text, controlled by the display setting, and a signature that the API uses to verify it. Blocks come before the text, so code must select them by type. The display setting trades visibility of reasoning against the size of stored content, while the signature must always survive. Passing the assistant's content back unchanged is the rule that keeps tool conversations valid. The next lesson moves to a tool that changes how Claude finds the tools it needs.

0 Comments

Reviewed before they appear

No comments yet.

Anthropic SDK Using Python
Anthropic SDK Intro Claude API Intro SDK Requirements Install Anthropic SDK Upgrade the SDK Create an API Key Set API Keys Use Environment Variables Import Anthropic Create a Client Send Your First Message Choose a Claude Model Set Max Tokens Read a Response Print Response Text Understand Message Blocks Use System Prompts Send User Messages Set Temperature Handle Stop Reasons Messages API Intro Create Messages Use Message Roles Send Conversation History Continue a Conversation Build a Chat Loop Reset Chat History Use Multiple Turns Inspect Message IDs Read Usage Data Write Clear Prompts Set a System Prompt Add Context to Prompts Use Prompt Templates Add Examples to Prompts Request JSON Output Use XML Tags Control Response Length Improve Prompt Quality Avoid Prompt Injection Read Text Blocks Read Content Blocks Check Stop Reasons Count Input Tokens Count Output Tokens Count Tokens Before Sending Limit Output Tokens Use Stop Sequences Read Request IDs Parse API Responses Streaming Intro Stream Text Output Use Stream Events Use Stream Helpers Get Final Messages Stream Async Responses Handle Stream Errors Build a Live Chat Stream Long Responses Close Streams Safely Build a Chatbot Keep Chat History Manage Chat Sessions Add Chat Memory Build a CLI Chatbot Build a Web Chatbot Use Flask with Claude Use FastAPI with Claude Return JSON from an API Add Chat Error Messages Async SDK Intro Create an Async Client Send Async Messages Use asyncio Run Parallel Requests Stream Async Text Handle Async Errors Set Async Timeouts Close Async Clients Choose Sync or Async Tool Use Intro Define a Tool Create Tool Schemas Send Tools to Claude Read Tool Calls Run Python Tools Return Tool Results Continue After Tool Use Control Tool Choice Handle Tool Errors Use the beta_tool Decorator Create a Tool Runner Add Tool Docstrings Validate Tool Inputs Use Multiple Tools Limit Tool Calls Log Tool Activity Build a Weather Tool Build a Search Tool Build a Calculator Tool Structured Output Intro Request JSON Data Validate JSON Output Use Pydantic Models Handle Invalid JSON Extract Fields from Text Generate Typed Data Use Image URLs Send Base64 Images Read PDFs with Claude Use Output Schemas Build Data Extraction Test Structured Responses Send Image Inputs Upload Files Use File IDs Process Text Files Summarize Documents Extract Table Data Handle File Errors Read Cache Usage Reduce Repeated Input Costs Debug Cache Misses Prompt Caching Intro Enable Prompt Caching Cache System Prompts Cache Long Context Test Cache Performance Use Cache Breakpoints Cache Best Practices Message Batches Intro Create a Message Batch Set Batch IDs Check Batch Status List Message Batches Get Batch Results Process Batch Outputs Handle Failed Batch Items Use Batches for Bulk Tasks Compare Batches and Requests Handle API Errors Handle Rate Limits Retry Failed Requests Set Request Timeouts Handle Network Errors Use Exponential Backoff Build Reliable API Calls Use Extended Thinking Log API Errors Track Request IDs Paginate API Results Set Thinking Budgets Read Thinking Blocks Use Tool Search Use Web Search Tools Use Computer Tools Use Citations Use Token Counting Read Raw Responses Set Custom Headers Use Claude on AWS Bedrock Use Claude on Vertex AI Use Claude on Microsoft Foundry Configure Cloud Credentials Choose a Cloud Provider Compare API Integrations Configure Provider Clients Handle Cloud Errors Use Cloud SDK Extras Deploy Cloud Apps Protect API Keys Use a .env File Ignore Secrets in Git Validate User Input Reduce Prompt Injection Limit Tool Permissions Avoid Unsafe Tool Calls Sanitize Tool Results Set Usage Limits Review Security Logs Test SDK Functions Mock API Responses Test Error Handling Test Streaming Code Measure Token Usage Track API Costs Configure Logging Deploy with Docker Deploy a FastAPI App Monitor Production Apps Build a Claude Chatbot Build a Text Summarizer Build a Code Explainer Build a JSON Extractor Build a PDF Assistant Build a Streaming Chat App Build a Tool-Calling Agent Build a Batch Processor Build a Research Assistant Build a Claude API Dashboard
Ask about this post
AI Ask about this post

Ask questions about Read Thinking Blocks and get answers drawn from it.

Signed-in readers only.