Fix Anthropic SDK IndexError When Streaming
The Error
When streaming responses from the Anthropic Python SDK, you encounter:
IndexError: list index out of range
This typically occurs when processing streaming events from responses that include tool use, extended thinking, or when the model returns unexpected content block types.
Quick Fix
Access content blocks safely with length checks:
import anthropic
client = anthropic.Anthropic()
with client.messages.stream(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello"}],
) as stream:
message = stream.get_final_message()
if message.content and len(message.content) > 0:
for block in message.content:
if block.type == "text":
print(block.text)
What’s Happening
The IndexError occurs when code assumes the response contains a specific number of content blocks at a specific index. Three scenarios commonly trigger this:
First, tool use responses contain tool_use content blocks instead of (or in addition to) text blocks. Code that accesses message.content[0].text fails when the first block is a tool_use block.
Second, extended thinking responses include thinking blocks before the text output. Accessing content[0].text hits a thinking block, which does not have a text attribute.
Third, the model may return an empty content array in edge cases, particularly when the response is interrupted or when using stop sequences that trigger before any content is generated.
Step-by-Step Fix
Step 1: Always check content block types
Never assume the type or position of content blocks:
message = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello"}],
)
for block in message.content:
if block.type == "text":
print(block.text)
elif block.type == "tool_use":
print(f"Tool call: {block.name}({block.input})")
Step 2: Handle streaming events correctly
When using the streaming API, process events by type:
with client.messages.stream(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": "Analyze this data"}],
tools=my_tools,
) as stream:
for event in stream:
if event.type == "content_block_start":
if event.content_block.type == "text":
pass # text block starting
elif event.content_block.type == "tool_use":
print(f"Tool: {event.content_block.name}")
elif event.type == "content_block_delta":
if hasattr(event.delta, "text"):
print(event.delta.text, end="")
Step 3: Use text_stream for simple text extraction
If you only need text output and want to skip tool use blocks:
with client.messages.stream(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello"}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
The text_stream property filters to only text delta events automatically.
Step 4: Handle tool use in streaming
For multi-turn tool use conversations, collect tool calls and process them:
with client.messages.stream(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=messages,
tools=tools,
) as stream:
response = stream.get_final_message()
# Process tool calls from the final message
for block in response.content:
if block.type == "tool_use":
result = execute_tool(block.name, block.input)
# Continue conversation with tool result
Step 5: Guard against empty responses
Always check that content exists before accessing:
message = stream.get_final_message()
if not message.content:
print("Empty response received")
elif message.stop_reason == "end_turn":
text_blocks = [b for b in message.content if b.type == "text"]
if text_blocks:
print(text_blocks[0].text)
elif message.stop_reason == "tool_use":
tool_blocks = [b for b in message.content if b.type == "tool_use"]
for tool in tool_blocks:
process_tool_call(tool)
Prevention
Never use hardcoded indices like content[0] without checking the block type. Always iterate over content blocks and filter by type. Use the SDK’s text_stream property for simple text extraction. Keep your SDK updated to get the latest content block type handling.
Level Up Your Claude Code Workflow
The developers who get the most out of Claude Code aren’t just fixing errors — they’re running multi-agent pipelines, using battle-tested CLAUDE.md templates, and shipping with production-grade operating principles.
Get Claude Code Mastery — included in Zovo Lifetime →
16 CLAUDE.md templates · 80+ prompts · orchestration configs · workflow playbooks. $99 once, free forever.