Você trocou o modelo das suas chamadas à API do Claude por um mais recente e agora aparece o erro abaixo. Ou o texto da resposta volta vazio. A causa costuma ser a mesma: o primeiro bloco do conteúdo (content[0]) não é mais texto, e sim um bloco de raciocínio (thinking).
AttributeError: 'ThinkingBlock' object has no attribute 'text'
Se você lê response.content[0].text com o SDK de Python, recebe esse erro. Em JavaScript, o mesmo código devolve undefined, então, em vez de falhar, uma string vazia percorre o seu aplicativo. A parte traiçoeira: com entradas simples o primeiro bloco continua sendo texto, então os testes com entradas leves passam.
O problema em três linhas
O que acontece
O primeiro bloco não é mais texto
Principalmente com entradas pesadas, a resposta começa com um bloco de raciocínio (às vezes também com entradas leves). content[0].text quebra.
Causa
Raciocina sem você pedir
Os modelos Claude 5 e posteriores raciocinam quando necessário mesmo que você não defina thinking. Os modelos Claude 4 não raciocinam se você não definir.
Correção
Procure os blocos pelo tipo
Não presuma o primeiro bloco. Reúna os blocos cujo type é "text" e verifique também o stop_reason.
1. Quando acontece
O caso típico é uma migração em que você só muda o nome do modelo. Por exemplo, você passa do Claude Sonnet 4.6 para o Claude Sonnet 5.5 reescrevendo apenas o valor de model. Supondo que o formato da resposta é o mesmo, você não mexe no resto do código. Então acontece o seguinte (o terceiro caso acontece até com código bem escrito).
| Como você lê | O que acontece |
|---|---|
content[0].text no SDK de Python | Falha com AttributeError: 'ThinkingBlock' object has no attribute 'text' |
content[0].text em JavaScript | Sem erro; vira undefined, e um texto vazio vai parar na tela ou no armazenamento |
| Procurando o bloco de texto | Se o modelo raciocinar muito e atingir max_tokens, pode não haver nenhum bloco de texto |
O terceiro caso acontece até com código bem escrito. Os tokens de raciocínio contam para o max_tokens, então, se o limite for pequeno, a resposta é cortada antes de o modelo escrever qualquer texto.
2. Causa: o Claude 5 e posteriores raciocinam sem você pedir
A documentação “Thinking” da Anthropic lista, modelo a modelo, o que acontece quando a requisição não inclui thinking. A linha divisória é o Claude 5.
| Modelo | Sem thinking | Conteúdo do raciocínio |
|---|---|---|
| Claude 5 e posteriores (Opus 5.5, Sonnet 5.5, Haiku 5.5, Fable 5.1 etc.) | Raciocina (raciocínio adaptativo) | Vazio por padrão (display: "omitted") |
| Opus 4.8, Opus 4.7 | Não raciocina | Só raciocina se você definir thinking: {type: "adaptive"}; o conteúdo vem vazio por padrão ("omitted") |
| Opus 4.6, Sonnet 4.6 | Não raciocina | Só raciocina se você definir thinking: {type: "adaptive"}; devolve um resumo por padrão ("summarized") |
Fonte: a tabela por modelo e a descrição de display em “Thinking”, da Anthropic (consultado em 1º de novembro de 2026). Opus 4.5, Sonnet 4.5 e Haiku 4.5 não aceitam {type: "adaptive"}; para fazê-los raciocinar, use {type: "enabled"} com budget_tokens.
O modelo decide a cada entrada se vai raciocinar. A documentação “Steering thinking” explica que, em perguntas factuais simples, o modelo pode responder sem bloco de raciocínio. E alerta para não construir a lógica do aplicativo supondo que toda resposta começa com um bloco de raciocínio. Ou seja, o primeiro bloco às vezes é texto e às vezes é raciocínio. Código que presuma qualquer um dos dois vai quebrar em algum momento.
O conteúdo da resposta é ordenado como mostrado abaixo. O conteúdo do raciocínio vem vazio por padrão, mas o bloco em si continua sendo devolvido.
Entrada leve (ex.: a capital do Japão?)
[0]textTóquioEntrada pesada (ex.: encontrar todos os números que atendem às condições)
[0]thinkingVazio, só a assinatura[1]textA respostaEntrada pesada que atinge max_tokens
[0]thinkingVazio, só a assinaturaSem textostop_reason: "max_tokens"3. O que encontrei nos testes
Testei no meu próprio ambiente com o SDK de Python (anthropic 1.14.0) em 1º de novembro de 2026. Enviei a quatro modelos uma entrada leve e uma pesada, uma vez cada, tanto sem thinking quanto com {type: "adaptive"}. Defini max_tokens como 8000. A entrada leve era uma pergunta factual simples que pedia a capital do Japão em uma só palavra (perguntamos em japonês); a pesada era um problema que pedia todos os inteiros de quatro dígitos que atendem a cinco condições.
| Modelo | Configuração de thinking | content (entrada leve) | content (entrada pesada) |
|---|---|---|---|
| Sonnet 4.6 | Não definido | text | text |
| Sonnet 4.6 | adaptive | text | só thinking (atingiu o limite) |
| Haiku 5.5 | Não definido | text | só thinking (atingiu o limite) |
| Haiku 5.5 | adaptive | text | só thinking (atingiu o limite) |
| Sonnet 5.5 | Não definido | text | thinking → text |
| Sonnet 5.5 | adaptive | text | thinking → text |
| Opus 5.5 | Não definido | text | thinking → text |
| Opus 5.5 | adaptive | thinking → text | thinking → text |
Em vermelho, as combinações em que content[0].text lançou AttributeError: 'ThinkingBlock' object has no attribute 'text'. Cada uma é o resultado de uma única execução; se o modelo raciocina ou não pode variar de uma execução para outra, mesmo com a mesma entrada.
Três conclusões.
Primeira: os modelos Claude 5 e posteriores raciocinaram na entrada pesada mesmo sem thinking. Com o Sonnet 4.6, omiti-lo manteve o texto como primeiro bloco. Isso confirma o que diz a documentação: mudar só o nome do modelo muda o formato da resposta.
Segunda: com a entrada leve, o primeiro bloco foi texto em quase todas as combinações. Se os seus testes usam só entradas leves, você não vai encontrar esse problema. Por outro lado, o Opus 5.5 com adaptive raciocinou até na entrada leve. No Claude 5 e posteriores, omitir thinking e definir adaptive são a mesma configuração, então essa diferença provavelmente se deve à variação entre execuções. Não dá para dizer que “entradas leves não fazem o modelo raciocinar”.
Terceira: ao atingir o limite, não voltou texto nenhum. Na entrada pesada, o Haiku 5.5 gastou todos os 8000 tokens raciocinando, tanto sem thinking quanto com adaptive, e não devolveu nenhum bloco de texto. O Sonnet 4.6 com adaptive também atingiu o limite. No Claude 5 e posteriores o conteúdo do raciocínio também vem vazio por padrão, então o usuário não vê nada, mas você continua pagando pelos tokens de saída.
4. Como corrigir
A correção é parar de presumir qual bloco vem primeiro e reunir os blocos do tipo texto (type é "text"). Verifique também o stop_reason para não tratar como resposta normal uma que foi cortada no limite.
Python
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=16000,
messages=[{"role": "user", "content": prompt}],
)
# Não presuma o primeiro bloco: reúna só os blocos de texto
text = "".join(b.text for b in response.content if b.type == "text")
if response.stop_reason == "max_tokens":
# Cortado no limite. text pode estar vazio
raise RuntimeError("Atingiu max_tokens. Aumente o limite ou reduza o raciocínio")
if response.stop_reason == "end_turn" and not text:
# Em fluxos com ferramentas, stop_reason é "tool_use" e texto vazio é normal
raise RuntimeError("Nenhum bloco de texto")
TypeScript
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const response = await client.messages.create({
model: "claude-sonnet-5-5",
max_tokens: 16000,
messages: [{ role: "user", content: prompt }],
});
// Filtre pelo tipo antes de ler text
const text = response.content
.filter((b): b is Anthropic.TextBlock => b.type === "text")
.map((b) => b.text)
.join("");
if (response.stop_reason === "max_tokens") {
throw new Error("Atingiu max_tokens. Aumente o limite ou reduza o raciocínio");
}
// Em fluxos com ferramentas, stop_reason é "tool_use" e texto vazio é normal
if (response.stop_reason === "end_turn" && text === "") {
throw new Error("Nenhum bloco de texto");
}
No SDK de TypeScript, os elementos de content são uma união de tipos como texto, raciocínio e uso de ferramentas. Escrever content[0].text diretamente normalmente não passa na verificação de tipos. Mas, se você forçar o tipo com as, o erro passa despercebido.
Defina max_tokens contando o raciocínio
A documentação “Steering thinking” explica que max_tokens é um limite conjunto para o raciocínio e o texto da resposta. Também observa que um limite escolhido supondo que o modelo não vai raciocinar costuma ser pequeno demais para entradas difíceis. Quando você recebe stop_reason: "max_tokens", há duas correções.
Os 16000 dos exemplos acima são uma referência. Nos meus testes, até 8000 às vezes foram gastos inteiros no raciocínio. Para produção, rode entradas pesadas e defina o valor olhando usage.output_tokens_details.thinking_tokens (o número de tokens gastos no raciocínio) na resposta.
- Aumentar o limite: quando o raciocínio melhora a qualidade da resposta nas entradas que foram cortadas
- Reduzir o esforço: quando o modelo só está raciocinando demais e a qualidade da resposta não muda (o esforço é definido com
output_config.effort)
Ao continuar uma conversa, devolva também os blocos de raciocínio
Ao acrescentar uma resposta ao histórico da conversa, não extraia só o texto; devolva o content como está. Principalmente em fluxos com ferramentas, o bloco de raciocínio guarda o motivo da chamada à ferramenta. A documentação pede que você devolva os blocos recebidos sem modificá-los, e blocos modificados são rejeitados com um erro 400. O mais seguro é manter separados o texto que você exibe e o content que devolve no histórico.
5. Dá para simplesmente desligar o raciocínio?
É tentador pensar “se o raciocínio atrapalha, é só desligar”, mas se dá ou não depende do modelo. Veja como thinking: {type: "disabled"} é tratado.
| Modelo | {type: "disabled"} |
|---|---|
| Opus 5.5, Fable 5.1, Fable 5 | Erro 400 (não dá para desligar) |
| Sonnet 5.5 | Erro 400 (em vez disso, {type: "between_tools"} pula o raciocínio inicial quando o esforço é high ou menor) |
| Haiku 5.5, Opus 5 | Dá para desligar (só com esforço high ou menor; erro 400 em xhigh e max) |
| Sonnet 5 | Dá para desligar |
Fonte: “Thinking”, da Anthropic (consultado em 1º de novembro de 2026). Também é possível verificar se um modelo aceita com capabilities.thinking.types.disabled.supported na Models API. O esforço padrão é medium no Opus 5.5 e no Haiku 5.5 e high na maioria dos outros modelos, então os padrões atendem à condição de “high ou menor”.
Mesmo nos modelos em que dá para desligar, fazer isso pode reduzir a qualidade da resposta em entradas difíceis. E o mesmo problema volta toda vez que você troca de modelo. A correção de fundo é um código que leia a resposta em qualquer ordem. Se quiser reduzir o raciocínio, reduza primeiro o esforço. Segundo a documentação da Anthropic, pedir ao Sonnet 5.5 no prompt de sistema que raciocine menos não reduz o raciocínio de forma confiável, e o Haiku 5.5 continuou raciocinando mesmo quando instruído a responder diretamente (nos testes da Anthropic). Para os dois, ajuste com o esforço.
6. Como não deixar passar nos testes
Esse problema escapa nos testes com entradas leves. Ao trocar de modelo, verificar nesta ordem torna mais difícil deixá-lo passar.
- Procure
content[0]no seu código: liste todos os lugares que leem por posição, comocontent[0],content[-1]e.content[0].text, não só no código de produção, mas também nos testes e nos logs - Acrescente entradas pesadas aos testes: inclua como entradas de teste as entradas mais longas de produção e as tarefas mais difíceis (só com entradas leves, o primeiro bloco continua sendo texto e a falha fica escondida)
- Use respostas simuladas que comecem com um bloco de raciocínio: nos testes unitários que não chamam a API, prepare uma resposta
[thinking, text]e outra só comthinkingestop_reason"max_tokens", para testar sem depender de o modelo raciocinar ou não - Faça a implantação aos poucos: não troque tudo de uma vez; passe primeiro parte das cargas de trabalho para o novo modelo, acompanhe as contagens de texto vazio e de exceções e depois amplie
⚠️ Quando vários serviços ou equipes trocam de modelo juntos, acrescente uma linha ao pedido de migração: “O primeiro bloco da resposta nem sempre é texto. Procure-o pelo tipo.” A mudança parece uma simples troca de nome, e se você esperar que cada equipe perceba sozinha, o código que passou nos testes com entradas leves vai quebrar em produção.
Isso não é a única coisa a verificar ao trocar de modelo. Para o que mudou na migração a partir de gerações anteriores, veja “Guia de Migração para Claude Opus 4.7” e “As 3 mudanças incompatíveis do Claude Fable 5.1”. O esforço na API é explicado na documentação “Effort” da Anthropic, e como escolher o esforço no Claude Code, em “Configuração de esforço do Claude Code”.
Resumo
AttributeError: 'ThinkingBlock' object has no attribute 'text'ocorre quando o primeiro bloco da resposta é um bloco de raciocínio e você tenta ler o texto comcontent[0].text- Os modelos Claude 5 e posteriores raciocinam quando necessário mesmo sem
thinking, então basta trocar o nome do modelo a partir de um Claude 4 para que o formato da resposta mude - Com entradas leves, o primeiro bloco costuma continuar sendo texto, então os testes leves passam
- A correção é reunir os blocos cujo
typeé"text"e verificar se ostop_reasoné"max_tokens" - Alguns modelos não permitem desligar o raciocínio, então a correção de fundo não é desligá-lo, e sim escrever código que leia a resposta em qualquer ordem
Perguntas frequentes
P. Nunca defini thinking. Por que recebo um bloco de raciocínio?
R. Nos modelos Claude 5 e posteriores, o raciocínio adaptativo fica ativo mesmo sem thinking, e o modelo raciocina nas entradas em que decide que precisa. O conteúdo do raciocínio vem vazio por padrão, mas o bloco em si é incluído na resposta. Modelos Claude 4, como o Claude Opus 4.8 e o Sonnet 4.6, não raciocinam se você não definir.
P. Por que recebo uma resposta sem nenhum bloco de texto?
R. Os tokens de raciocínio contam para o max_tokens, e o limite foi atingido antes de o modelo escrever qualquer texto. O stop_reason deve ser "max_tokens". Aumente o limite ou reduza o esforço para diminuir o raciocínio.
P. E se eu quiser ler o conteúdo do raciocínio?
R. Defina thinking: {type: "adaptive", display: "summarized"} para receber um resumo do raciocínio. O padrão no Claude 5 e posteriores é "omitted" (o conteúdo vem vazio e só a assinatura é incluída). De qualquer forma, você é cobrado por todos os tokens de raciocínio.
Histórico de atualizações
- 1º de novembro de 2026Primeira versão. Escrita após revisar a documentação “Thinking” e “Steering thinking” da Anthropic e testar a ordem da resposta de quatro modelos no meu próprio ambiente.
Fontes
- Anthropic: Thinking, Steering thinking