Claude SSE Parse Error — Streaming Fix Guide (2026) | AI Error Hub
Anthropic Claude Streaming Severity: High Client-side

Claude SSE Parse Error During Streaming

Your streaming client fails to parse Server-Sent Events from Claude. Symptoms: JSONDecodeError on partial chunks, missed events, incomplete responses, or hangs when Anthropic sends keep-alive pings. Root cause: custom parsers that don't handle SSE spec properly.

Error surface: Client SSE parser Category: Streaming Transport: Server-Sent Events (SSE) Last tested: Nov 15, 2026
Quick Fix (TL;DR)

Use the official SDK's stream() method — it handles SSE parsing correctly. If you must write a custom parser: split on \n\n (event boundary), parse event: and data: lines, ignore ping events, and never assume one TCP chunk = one SSE event.

The Error Message You're Seeing

Common failure modes
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
  → Tried to json.loads() an "event: message_start" line

json.decoder.JSONDecodeError: Unterminated string starting at ...
  → Parsed a chunk that was split mid-JSON across TCP frames

TypeError: 'NoneType' object is not subscriptable
  → Ignored an event type and got None from dict lookup

Stream hangs indefinitely
  → Not handling "ping" keep-alive events, blocking on read

UnicodeDecodeError: 'utf-8' codec can't decode byte
  → Split a multi-byte character across chunks

Understanding Claude's SSE Format

Raw SSE stream from api.anthropic.com
event: message_start data: {"type":"message_start","message":{"id":"msg_01...",...}} event: content_block_start data: {"type":"content_block_start","index":0,...} event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello"}} event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" world"}} event: ping data: {"type": "ping"} event: content_block_stop data: {"type":"content_block_stop","index":0} event: message_delta data: {"type":"message_delta","delta":{"stop_reason":"end_turn"},"usage":{...}} event: message_stop data: {"type":"message_stop"}

What Actually Causes This Error

40%
Assuming one HTTP chunk = one SSE eventTCP fragments events across chunks. You must buffer until you see \n\n.
25%
Trying to parse event: line as JSONOnly the data: line contains JSON. The event: line is the event type.
18%
Not handling ping eventsAnthropic sends keep-alive pings every ~15s. Ignoring them silently is fine; crashing on them is not.
10%
UTF-8 boundary splitsEmoji or non-ASCII text split across TCP chunks. Decode after buffering, not before.
7%
Not accumulating input_json_delta for tool_useTool inputs stream as JSON fragments; must concatenate before parsing.

Fixes That Work (Tested Nov 2026)

1Use the SDK's Streaming Helper

Python · SDK handles SSE correctly
import anthropic client = anthropic.Anthropic() with client.messages.stream( model="claude-sonnet-4-5", max_tokens=1024, messages=[{"role": "user", "content": "Hello"}] ) as stream: for text in stream.text_stream: print(text, end="", flush=True) # After stream completes, get full message final = stream.get_final_message() print(f"\nUsage: {final.usage}")
TypeScript · SDK streaming
const stream = client.messages.stream({ model: "claude-sonnet-4-5", max_tokens: 1024, messages: [{ role: "user", content: "Hello" }] }); for await (const event of stream) { if (event.type === "content_block_delta" && event.delta.type === "text_delta") { process.stdout.write(event.delta.text); } } const final = await stream.finalMessage();

2Robust Custom SSE Parser (Python)

Python · manual SSE parsing
import httpx, json def stream_claude(messages): headers = { "x-api-key": API_KEY, "anthropic-version": "2023-06-01", "content-type": "application/json", } body = { "model": "claude-sonnet-4-5", "max_tokens": 1024, "messages": messages, "stream": True, } with httpx.stream("POST", "https://api.anthropic.com/v1/messages", headers=headers, json=body) as r: buffer = "" for chunk in r.iter_text(): buffer += chunk # Events end with \n\n while "\n\n" in buffer: event_text, buffer = buffer.split("\n\n", 1) yield from parse_event(event_text) def parse_event(text): event_type = None data = None for line in text.split("\n"): if line.startswith("event: "): event_type = line[7:] elif line.startswith("data: "): data = json.loads(line[6:]) if event_type and data: yield event_type, data # Use it: for event_type, data in stream_claude([...]): if event_type == "content_block_delta": if data["delta"]["type"] == "text_delta": print(data["delta"]["text"], end="") elif event_type == "ping": continue # keep-alive, ignore

3Handle Ping Events and Bytes Correctly

Common gotchas checklist

1. Use iter_bytes() then decode UTF-8 after buffering, not per-chunk decode
2. Buffer until you see \n\n — never assume atomicity
3. Only data: lines contain JSON
4. Ignore ping events (or log them if debugging keep-alives)
5. Handle error events explicitly — Anthropic sends these mid-stream on failures
6. The stream ends with message_stop, not connection close

Preventing This Error Going Forward

  • Prefer the SDK. It handles all edge cases including reconnection.
  • Buffer bytes, decode on event boundaries. Never decode partial chunks.
  • Whitelist known event types. Ignore unknown ones for forward compatibility.
  • Log ping events count. Useful for debugging network stability.
  • Set a read timeout, not just connect. Detect stalled streams.
AR
Tested by Ahmed R.
Researcher · AI Error Hub
Last verified: Nov 15, 2026 · SDK: anthropic-sdk-python 0.42

Frequently Asked Questions

Keep-alive to prevent proxies (Cloudflare, corporate firewalls) from closing idle connections during long responses. Pings arrive roughly every 15 seconds during model thinking or between content blocks.

Not directly — EventSource doesn't support POST or custom headers. Use fetch() with ReadableStream, or better, proxy through your backend using SSE from server to browser. See our CORS error guide for the pattern.

That's a connection drop. Check for network errors, timeouts, or an error event that arrived before close. The SDK auto-detects and raises; custom parsers should treat premature close as an error and consider retry with same request ID.

Only for very slow consumers. If your loop reads events fast enough, TCP handles it. If you're writing each token to a slow database, buffer or batch instead — Anthropic may close the connection if you're too slow (see streaming-backpressure-timeout error).

Yes, both use the anthropic SDK internally. LangChain's ChatAnthropic.stream() yields message chunks. No SSE handling needed on your side — they abstract it fully.