[Bug] VSCode extension crashes on unknown Anthropic SSE event type — "Unhandled case: [object Object]"
[Bug] VSCode extension crashes on unknown Anthropic SSE event type — "Unhandled case: [object Object]"
Environment
- Extension :
anthropic.claude-codev2.1.141 (darwin-arm64), installed 2026-05-14 06:24 UTC - VSCode host : Code (Stable)
- OS : macOS 15.4 (Darwin 25.4.0), arm64
- Model : Claude Opus 4.7 1M context (
claude-opus-4-7[1m]) - Project type : long-running conversation (~500+ exchanges) with extensive tool use and MCP servers (Playwright, Chrome DevTools, Figma)
Symptom
During an active streaming response from the API, a red banner pops up in the conversation pane :
⚠️ Unhandled case: [object Object] View output logs · Troubleshooting resources
The agent's current in-flight response is truncated and no further tool calls execute. The conversation appears stuck until the user sends a new prompt (which restarts a fresh streaming session — sometimes the error re-triggers immediately).
This is reproducible but the trigger is non-deterministic from a user perspective — it depends on which SSE event the API server emits.
Root cause (analysis)
Decompiled the extension bundle :
File : ~/.vscode/extensions/anthropic.claude-code-2.1.141-darwin-arm64/webview/index.js
Function : XB1.processStreamEvent($) (~ offset 3143657)
processStreamEvent($) {
switch ($.type) {
case "message_start": /* … */ break;
case "message_delta": /* … */ break;
case "content_block_start": /* … */ break;
case "content_block_delta": /* … */ break;
case "content_block_stop": /* … */ break;
case "message_stop": /* … */ break;
default: QB1($) // ← throws "Unhandled case: ${$}"
}
}
And the assertNever-style helper :
function QB1($, Z) {
throw Error(Z ?? `Unhandled case: ${$}`)
}
The Anthropic Messages SSE protocol documents several additional event types that the switch does not cover, notably :
ping(periodic keepalive — sent during long thinking blocks / extended reasoning)error(server-side error frames)
…plus any new event type Anthropic may introduce server-side (e.g. tied to the 1M context window, prompt caching telemetry, or extended thinking compaction). Because QB1 template-coerces the $ argument to string, an object event yields [object Object] — confirming the value is the raw event object rather than a string type discriminator.
A second switch at ~ offset 3145059 (content_block_delta sub-type dispatch on thinking_delta / signature_delta / compaction_delta) has the same defensive pattern and is also vulnerable to forward-compat breaks.
Minimal repro
Deterministic repro requires the API to emit one of the unhandled event types. Observed in practice on :
- Long thinking blocks with Opus 4.7 1M context (probably
ping) - Sustained MCP-heavy sessions (Playwright Make UI automation with multi-second wait loops)
A reliable synthetic repro :
- Patch a local proxy between the extension and
api.anthropic.com(e.g.mitmproxy --mode reverse:https://api.anthropic.com) - Inject a custom SSE event line into a streaming response :
````
event: ping
data: {"type":"ping"}
- Observe the extension crash with
Unhandled case: [object Object]
Expected behavior
Per SSE protocol guidance and the documented Messages API behavior, unknown event types should be ignored by the consumer to preserve forward compatibility. The current behavior couples the extension's stability tightly to the server-side event taxonomy and breaks every time Anthropic introduces a new event type.
Suggested fix
Replace the default: QB1($) throw with a silent log + skip in both switches in XB1.processStreamEvent and the content_block_delta sub-type dispatcher :
default:
console.debug("Ignoring unknown SSE event", $);
break;
Optionally surface unknown events through telemetry so the team can spot new types proactively, but never crash the UI on them.
The same assertNever pattern is appropriate inside the codebase for exhaustive type checks against TypeScript discriminated unions where the input is fully owned — but it is the wrong abstraction at protocol boundaries with an evolving external service.
Workaround applied locally
Patched QB1 to log instead of throw :
// before
function QB1($,Z){throw Error(Z??`Unhandled case: ${$}`)}
// after
function QB1($,Z){console.warn("[CC patch — Unhandled SSE event silenced]",$,Z);}
Idempotent re-apply script (in case auto-update reverts) :
https://gist.github.com/Gavan31/a9f91abc04b75a45e4da95f43ebdb1af
Patch survives the JS syntax checker (node --check) and restores the conversation flow. Open question : whether downstream consumers of the now-undefined return value still behave correctly — preliminary observation says yes, but not exhaustively tested.
Severity
High — every long-running CC session is vulnerable. The error is silent for the user (no obvious clue what to do) and persists until the extension is updated or patched. Reload Window is not sufficient by itself — the SSE event will simply re-arrive on the next API call.
Related
- Likely affects all extension versions ≤ 2.1.141
- Probably affects the JetBrains plugin and the CLI in their respective SSE parsers — would be worth a coordinated audit
---
Reported by Alexandre Goubault (alex@gavan.fr), 2026-05-14. Diagnosis assisted by Claude Code (Opus 4.7 1M context) during a Lea-asso POC session.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗