VSCode extension v2.1.141: "Unhandled case: [object Object]" banner from assertNever on unknown streaming-event types

Resolved 💬 21 comments Opened May 14, 2026 by basje01 Closed May 14, 2026

Summary

webview/index.js in the VSCode extension has a TypeScript assertNever-style exhaustive-check (helper QB1) over Anthropic streaming-event types. When the server emits an event type the switch doesn't cover, the helper throws Error(\Unhandled case: ${J}\) interpolating the whole event object instead of its .type field — producing the user-visible banner text:

Unhandled case: [object Object]
View output logs · Troubleshooting resources

The banner is non-dismissable except via the manual ×, persists across subsequent tool calls, and is alarming to operators who reasonably assume the session is broken. The underlying session and the model are unaffected — it's a renderer-side throw, not a data-flow problem.

Environment

  • Extension: anthropic.claude-code v2.1.141
  • Platform: darwin-arm64 (macOS)
  • Bundle path: ~/.vscode/extensions/anthropic.claude-code-2.1.141-darwin-arm64/webview/index.js

Source of the throw

// Helper (assertNever pattern, emitted by bundler)
function QB1($, Z) { throw Error(Z ?? `Unhandled case: ${$}`) }

// Caller — switch over streaming-event types
switch (J.type) {
  case "signature_delta":   { /* ... */ break }
  case "compaction_delta":  break
  // ...other cases...
  default: QB1(J)   // ← passes whole event object, not J.type
}

The template-string interpolation ${$} calls .toString() on the event object, yielding the literal "[object Object]".

Known-handled cases in v2.1.141

Extracted from the bundle (last ~3 KB before the Unhandled case literal):

text, text_delta, thinking_delta, signature_delta, citations_delta, input_json_delta, compaction_delta, content_block_start, content_block_delta, content_block_stop, message_start, message_delta, message_stop.

Likely missing cases

  • ping — Anthropic streaming keepalive event
  • error — server-side error events
  • Any new content-block-delta type shipped after the v2.1.141 build cutover

A real reproduction of the offending event requires capturing the VSCode extension output log at the moment of the throw — I have not done that here. The diagnosis is grounded in static source inspection.

Suggested fixes

1. Format the error usefully (1-line change):

function QB1($, Z) {
  throw Error(Z ?? `Unhandled case: ${$?.type ?? JSON.stringify($)}`)
}

Operators would then see e.g. Unhandled case: ping instead of Unhandled case: [object Object], making the bug self-reporting.

2. Soften the failure mode (recommended in addition to #1):

Streaming events the renderer doesn't recognize should be logged to the output channel and skipped, not thrown as a top-of-window banner. Hard-throwing on every new server-side event type couples extension stability to backend compatibility — every Anthropic API addition risks a banner storm in older extension versions still in the field.

Repro

The banner fires intermittently during long-running sessions with many streaming responses. I have not produced a deterministic repro (would need to inject a streaming event of an unknown type).

Diagnosis trail (for reproducibility)

# Confirm extension version
ls ~/.vscode/extensions/ | grep claude-code

# Find the throwing helper
grep -n "Unhandled case" ~/.vscode/extensions/anthropic.claude-code-2.1.141-*/webview/index.js

# Enumerate the switch's known cases
python3 -c "
import re
data = open('$HOME/.vscode/extensions/anthropic.claude-code-2.1.141-darwin-arm64/webview/index.js').read()
m = re.search(r'Unhandled case', data)
chunk = data[max(0, m.start()-3000):m.start()]
print(sorted(set(re.findall(r'case\"([^\"]+)\"', chunk))))
"

Note on a red herring

In a prior internal investigation we briefly hypothesized that the banner was triggered by the harness's <persisted-output> wrapper (the marker shown when a tool result is spilled to disk because it exceeds the inline display cap). That hypothesis was falsified: the string persisted-output does not appear anywhere in the extension bundle — the wrapper is harness-side text the extension just renders as a string, and is unrelated to the streaming-event switch.

View original on GitHub ↗

21 Comments

carlosresu · 2 months ago

Is there a workaround for this? It's happening on my Windows 11 Business dev machine as well.

carlosresu · 2 months ago
Is there a workaround for this? It's happening on my Windows 11 Business dev machine as well.

I'm also experiencing it on Windows 11 Business. You guys on Windows can run this command to patch it for now

Patch 1 — readable banner text:

$f = "$env:USERPROFILE\.vscode\extensions\anthropic.claude-code-2.1.141-win32-x64\webview\index.js"
(Get-Content -Raw $f).Replace(
  'throw Error(Z??`Unhandled case: ${$}`)',
  'throw Error(Z??`Unhandled case: ${$?.type??JSON.stringify($)}`)'
) | Set-Content -NoNewline $f

Patch 2 — suppress inner content-block-delta banner:

$f = "$env:USERPROFILE\.vscode\extensions\anthropic.claude-code-2.1.141-win32-x64\webview\index.js"
(Get-Content -Raw $f).Replace(
  'case"compaction_delta":break;default:QB1(J)',
  'case"compaction_delta":break;default:console.warn("[claude-code] Unhandled streaming event:",J?.type??JSON.stringify(J))'
) | Set-Content -NoNewline $f

Patch 3 — suppress outer streaming-message banner:

$f = "$env:USERPROFILE\.vscode\extensions\anthropic.claude-code-2.1.141-win32-x64\webview\index.js"
(Get-Content -Raw $f).Replace(
  'default:QB1($)',
  'default:console.warn("[claude-code] Unhandled streaming event (outer):",$?.type??JSON.stringify($))'
) | Set-Content -NoNewline $f
JCBauza · 2 months ago

Confirming on macOS-arm64, VSCode-Insiders, extension v2.1.141 (Anthropic publisher build, not a fork). Hit this today (2026-05-13 20:22 PST) mid-session against claude-opus-4-7[1m] via Anthropic direct API (not Bedrock), so it's not Bedrock-specific despite #52151's witness.

Two distinct call sites

QB1 is reached from two switch statements in webview/index.js, both worth fixing:

// 1. Top-level SSE event router — XB1.processStreamEvent
switch ($.type) {
  case "message_start": ...
  case "message_delta": ...
  case "content_block_start": ...
  case "content_block_delta": { ... b20(Z, $); ... }
  case "content_block_stop": ...
  case "message_stop": ...
  default: QB1($)   // ← passes the whole event object; `ping`/`error` land here
}

// 2. content_block_delta type router — b20
switch (J.type) {
  case "text_delta" / "citations_delta" / "input_json_delta" /
       "thinking_delta" / "signature_delta" / "compaction_delta": ...
  default: QB1(J)   // ← any new delta type lands here
}

The first one is the wider concern: standard Anthropic streaming routinely emits ping keepalives and error events, neither of which has a case here. A single ping from the API throws this banner.

macOS/bash patch (counterpart to the PowerShell one upstream)

F=~/.vscode-insiders/extensions/anthropic.claude-code-2.1.141-darwin-arm64/webview/index.js
# Or for stable: ~/.vscode/extensions/anthropic.claude-code-2.1.141-darwin-arm64/webview/index.js
cp "$F" "$F.bak"
# Warn-and-continue (recommended fix #2 from the issue body):
perl -i -pe 's|function QB1\(\$,Z\)\{throw Error\(Z\?\?`Unhandled case: \$\{\$\}`\)\}|function QB1(\$,Z){try{console.warn("[Claude Code patch] Unhandled SSE case (warn-only):",\$?.type??\$,JSON.stringify(\$))}catch(e){console.warn("[Claude Code patch] Unhandled SSE case (unstringifiable):",\$?.type??\$)}}|' "$F"

This is more aggressive than the PowerShell snippet above (which only formats the error usefully but still throws). For long-running sessions where a single unknown event would otherwise tear down the UI mid-stream, warn-and-continue restores resilience. Tradeoff: future genuinely-malformed events become silent — but they'll be visible in the Output → "Claude VSCode" log as a [warn], which is the correct severity for "renderer doesn't know about this event type yet."

Suggested doctrine for the official fix

The exhaustive-check pattern is great for catching API drift in dev, but in shipped extensions it should fail soft (log + skip), because the extension lifecycle is much longer than the API lifecycle. Every new server-side event type would otherwise cause a banner storm in older extensions still installed in the field. Suggest both:

  1. Format the error usefully (suggested fix #1 — already covered by the existing comment).
  2. Default to warn-and-skip in production, with an opt-in env var (e.g. CLAUDE_CODE_STRICT_SSE=1) that restores the throw for SDK developers who want to catch protocol drift early.
TshyGO · 2 months ago

Confirming this reproduces on win32-x64 as well (the issue only documents darwin-arm64), and as a real user-facing occurrence — not static analysis only.

  • Extension: anthropic.claude-code v2.1.141 (win32-x64)
  • Bundle: ~/.vscode/extensions/anthropic.claude-code-2.1.141-win32-x64/webview/index.js
  • Context: banner appeared mid-stream during a normal interactive session in a VSCode webview tab

Independent static inspection of the win32 bundle matches your findings exactly:

  • Same helper: function QB1($,Z){throw Error(Z??Unhandled case: ${$})}
  • Same call site: switch(J.type){ ... default:QB1(J) } — the whole event object is passed, not J.type
  • Delta-type cases handled in this build: text_delta, citations_delta, input_json_delta, thinking_delta, signature_delta, compaction_delta → then default:QB1(J)

So the bug is platform-independent (shared webview bundle, identical code on win32 and darwin) and does fire in normal use, not just in theory.

+1 on both suggested fixes — especially #2 (log-and-skip instead of hard-throw): the throw currently aborts rendering of the in-flight streamed message in that tab, so a single unrecognized event type silently loses the assistant's reply for that turn.

arthur-mayor · 2 months ago

Adding a Windows data point + an additional trigger path that confirms your diagnosis.

Environment

  • Extension: anthropic.claude-code v2.1.141
  • Platform: win32-x64 Windows 11
  • Bundle path: ~/.vscode/extensions/anthropic.claude-code-2.1.141-win32-x64/webview/index.js
  • Backend: Opus 4.7 1M context via direct Anthropic API (not Bedrock)

Banner reproduces identically on Windows. Issue is platform-independent (as expected from your static analysis of the renderer bundle).

Additional trigger path (hook-output side)

In our project we have ~30 user-defined hooks (UserPromptSubmit, SessionStart, etc) that read append-only JSONL log files. When one of those JSONL files is left in a UU (merge conflict unresolved) state, the hook's json.loads(line) crashes on the conflict marker lines, the hook returns a malformed payload, and the banner fires.

This is consistent with your hypothesis that the throw catches event types the switch doesn't cover — in our case the unknown "event" likely originates from hook-output parsing on the extension side, not from a streaming-event missed type. Either way the renderer-side fix (#1 in your suggested fixes) catches both classes:

function QB1($, Z) {
  throw Error(Z ?? `Unhandled case: ${$?.type ?? JSON.stringify($)}`)
}

would have shown us Unhandled case: <hook event type> or the stringified payload — instant diagnosis instead of 30+ minutes of log archaeology.

Workaround we adopted (until extension fix lands)

Two-layer defense on the userland side:

  1. .gitattributes with merge=union for append-only JSONL files so concurrent appends from different branches intercalate automatically instead of producing conflict markers.
  2. Pre-commit hook that blocks any commit containing literal conflict markers in staged files.

This eliminates the upstream condition that fed malformed payloads to the extension. It does not address the renderer-side fragility — your suggested fixes (#1 useful error message + #2 log-and-skip unknown events) remain the proper resolution.

Happy to provide an extension output log if a deterministic repro becomes useful — would need guidance on what level to capture (the user-facing "View output logs" doesn't show the throw site).

ancientGlider · 2 months ago
Edit (correction): my original point #2 below was wrong. The tool calls I saw after the banner were from parallel Task subagents, not from the main stream. The main stream was lost, matching @JCBauza and @TshyGO. Strikethrough corrections inline; rest of the data points stand. Sorry for the noise.

+1 reproducing on darwin-arm64, VSCode stable, extension v2.1.141.672, Opus 4.7 1M via direct Anthropic API (matches OP's environment). Two data points that don't appear upthread:

1. Trigger correlation: a 33s stream stall immediately precedes the throw

Relevant slice of the Output → "Claude VSCode" log (timestamps in UTC, banner appeared shortly after \14:11:20\ local):

\\\
14:10:09.980 [WARN] Streaming completed with 1 stall(s), total stall time: 33.0s
14:10:09.982 [DEBUG] Fast mode unavailable: Fast mode requires extra usage billing
14:11:20.474 Received message from webview: open_output_panel ← user clicked \"View output logs\" from the banner
\
\\

So the timeline is streaming finishes with a long stall → ~70s of idle → banner. Not \"mid-stream\", not \"on stream start\" — after a stall-recovered completion. Worth checking whether stall-recovery emits a synthetic event (or a delayed \ping\/\error\) that the outer \XB1.processStreamEvent\ switch doesn't cover.

2. ~~Backend kept executing tool calls after the banner — UI throw did NOT abort the session~~ → Retracted, see edit above

~~Contrary to the symptoms from @JCBauza (\"tear down the UI mid-stream\") and @TshyGO (\"silently loses the assistant's reply for that turn\"), in my case the agent kept working while the banner was up.~~ Correction: the main stream was lost the same way they describe. The tool-dispatch lines I saw after the banner —

\\\
14:11:23 tool_dispatch_start tool=Write ...
14:11:29 tool_dispatch_start tool=TodoWrite ...
14:11:29 [API REQUEST] /v1/messages ...
\
\\

— were emitted by parallel \Task\ subagents running their own event loops, not by the main session that hit the throw. So my data point becomes the narrower (but still useful) one: the throw is scoped to one session's renderer, not the host process — sibling subagent loops in the same window are unaffected. The main stream behaves exactly as described upthread.

3. Not hook-output related

Excluding @arthur-mayor's hook-output trigger path for this repro: \[DEBUG] Hooks: Found 0 total hooks in registry\ — no user-defined hooks configured in this project, and the banner still fires. Confirms there are multiple independent trigger paths into \QB1\.

Quick env summary

| Field | Value |
|---|---|
| OS | macOS Darwin 25.3.0 (arm64) |
| Extension | \anthropic.claude-code\ v2.1.141.672 |
| Entrypoint | \claude-vscode\ (per \x-anthropic-billing-header\) |
| Model | \claude-opus-4-7[1m]\ |
| Backend | Anthropic direct API |
| Hooks | 0 |
| MCPs active | none (only cached \claude.ai Google Drive\ skipped on needs-auth) |
| Context | \tokens=[REDACTED] level=ok effectiveWindow=980000\ (well below limit) |
| Concurrency | parallel \Task\ subagents active at the time of throw |

treyball53 · 2 months ago

Adding another macOS arm64 data point plus a behavioral note + objective hot-patch verification, after independently reproducing @JCBauza and @carlosresu's analysis.

Environment

  • Extension: anthropic.claude-code v2.1.141 (darwin-arm64), Anthropic publisher build
  • Platform: macOS Sequoia (Darwin 25.4.0), Apple Silicon
  • Backend: Claude Opus 4.7 1M via direct Anthropic API (no Bedrock/Vertex)
  • VSCode: Stable channel

Independent reverse-engineering confirms the call-site layout

Same QB1($, Z) { throw Error(Z ?? \Unhandled case: ${$}\) } definition, same two default:QB1(...) call sites at byte offsets 3144077 (outer processStreamEvent switch over $.type) and 3145478 (inner content_block_delta switch over J.type) in webview/index.js. The QB1 function definition itself sits at byte 3145777 — a third occurrence — which the patches below leave intact so any legitimate throw site outside the two default arms still works.

Behavioral observation (didn't see this in the thread yet)

Manual "resume" of a stalled session is itself susceptible: the new stream proceeds fine until the next unrecognized event/delta type lands, at which point the banner fires again and the stream halts. So a single long task with multiple thinking/tool segments can hit the banner 3–5 times in succession, each "resume" advancing a bit further before hitting the next unhandled variant. This makes the bug feel non-deterministic ("sometimes it works"), but the pattern is consistent — it's just that each task has a different distribution of recognized vs. unrecognized event types arriving.

macOS arm64 hot-patch (Patch-2 equivalent of @carlosresu's PowerShell)

F="$HOME/.vscode/extensions/anthropic.claude-code-2.1.141-darwin-arm64/webview/index.js"
cp "$F" "$F.bak-2026-05-14"
# Both default arms (outer SSE switch + inner content_block_delta switch):
sed -i '' 's/default:QB1(\$)/default:break/; s/default:QB1(J)/default:break/' "$F"

Then VSCode Developer: Reload Window.

Objective verification (so anyone replicating can sanity-check)

| Check | Pre-patch | Post-patch |
|---|---|---|
| SHA256 (first 16) | d756d1d369cfb41a | b46413d05cf855d2 |
| File size (bytes) | 4,798,391 | 4,798,389 |
| default:QB1( occurrences | 2 | 0 |
| default:break occurrences | 4 | 6 |
| QB1 function definition at 3145777 | present | preserved |

File-size delta is -2 bytes (QB1($) 6 chars → break 5 chars × 2 sites).

Banner has not recurred across either of my two VSCode windows since reload (~2 hours of mixed cc-ops and other-project usage). Will report back if anything regresses before 2.1.142+ ships with the proper switch enumeration.

Thanks for the canonical write-up @JCBauza and the workaround template @carlosresu — the byte-offset detail saved me a lot of grepping.

SvenRieke · 2 months ago

Additional repro: Read tool offset passed as array triggers this

Filing from #59101 (closing as duplicate of this).

We identified a specific trigger for this crash: the model occasionally generates a Read tool call with offset as an array (e.g., [1500]) instead of a plain number (1500). The tool input validator catches it, but the extension UI then hits the unhandled case instead of rendering a graceful error.

Log evidence (exthost/Anthropic.claude-code/Claude VSCode.log):

Read tool input error: Read failed due to the following issue:
The parameter `offset` type is expected as `number` but provided as `array`

Occurred twice in a single session within ~5 minutes. Each time it was immediately followed by the "Unhandled case: [object Object]" banner and session freeze.

So there are at least two distinct triggers for the same UI crash:

  1. Unknown SSE event types (your original report)
  2. Schema validation errors on tool input (this one)

Both suggest the assertNever / unhandled-case path in the renderer needs a catch-all fallback.

The-best-of-us · 2 months ago

I'm having the same issue with the unknown unhandled case. For me, I believe it started when I last updated VS Code this week. Did anyone else have this issue inside of VS Code and get this after the update from VS Code?

Personally the disruption caused by whatever the unhandled case is more painful to me, but knowing what Claude thinks the issue is would also be useful.

The-best-of-us · 2 months ago

Inspired by the other person in this thread who created a patch for this issue, I had Claude Code create one for me, too, that would be a bit more informative of what was going on so I could address the actual issue underneath. So here's the pseudocode my Claude created as a workaround. Maybe you can get your Claude to help you to understand the root cause of the error on your machine as well, until Anthropic has an actual fix.

How to apply (on windows 11)

Transcribe the pseudocode replacement below into a PowerShell one-liner
similar to the patches already in this thread, with these requirements:

  • Back up index.js to index.js.bak before modifying
  • Use Set-Content -NoNewline -Encoding UTF8 (PS 5.x defaults can write

UTF-16/BOM and corrupt the JS file)

  • Close all VS Code windows before applying so the webview reloads cleanly
  • Verify the patched file contains the string "[claude-code:unhandled-event]"

before reopening

Diagnostic patch for [object Object] banner — single-point variant

A consolidated alternative to the existing patches in this thread. Modifies only the QB1 helper function in webview/index.js, so both calling sites (the inner content-block-delta switch and the outer streaming-message switch) automatically benefit.

What it does

  • Banner still fires — failure remains visible. (Patches 2 + 3 in this thread silently suppress the banner, which trades cosmetic relief for diagnostic blindness.)
  • Banner text becomes informative — shows event.type instead of literal [object Object].
  • Full event JSON is captured forensically in two places:
  • Webview DevTools console — tagged [claude-code:unhandled-event], live, filterable.
  • Webview localStorage — key claude_code_unhandled_events, rolling last 50 entries, survives webview reloads.

This is especially useful for users hitting this banner from different triggers (the thread now confirms at least two: unknown SSE event types and tool-input validation errors). Capturing the actual event content lets users distinguish which class of failure they're seeing.

Pseudocode

// Original (single line in bundled minified JS):
function QB1($, Z) {
  throw Error(Z ?? `Unhandled case: ${$}`)
}

// Replacement (functionally equivalent throw, plus diagnostic capture):
function QB1(event, optionalMessage) {
  try {
    const record = {
      ts:             new Date().toISOString(),
      type:           event?.type ?? "(no type)",
      payload:        event,
      contextMessage: optionalMessage ?? null
    };

    // Forensic side-channel 1: webview DevTools console
    console.error("[claude-code:unhandled-event]", JSON.stringify(record));

    // Forensic side-channel 2: webview localStorage, rolling 50-entry window
    try {
      const key   = "claude_code_unhandled_events";
      let stored  = JSON.parse(localStorage.getItem(key) ?? "[]");
      stored.push(record);
      if (stored.length > 50) stored = stored.slice(-50);
      localStorage.setItem(key, JSON.stringify(stored));
    } catch { /* swallow */ }
  } catch { /* swallow diagnostic errors so they never break the throw path */ }

  // Re-throw with informative message — banner is now readable
  throw Error(
    optionalMessage ??
    `Unhandled case: ${event?.type ?? (JSON.stringify(event) ?? "").slice(0, 200)}`
  );
}

How to inspect captured events

After the banner fires:

Command Palette → "Developer: Open Webview Developer Tools" → Console tab

Then either:

  • Filter the console for [claude-code:unhandled-event] (live entries during the session), or
  • Paste this to get the persisted history across reloads:

``javascript
JSON.parse(localStorage.getItem('claude_code_unhandled_events'))
``

Benefits vs other patches in this thread

| Approach | Banner visible? | Banner readable? | Forensic capture? | Modification points |
|---|---|---|---|---|
| Patch 1 (in-thread) | Yes | Yes (event type) | No | 1 (QB1) |
| Patches 2 + 3 (in-thread) | No | N/A | Console only, both switches | 2 (both switches) |
| This patch | Yes | Yes (event type) | Yes (console + localStorage, persistent) | 1 (QB1) |

Caveats (unchanged from other in-thread patches)

  • Userland modification of the bundled extension. Will be reverted on next extension auto-update.
  • The path includes the version (anthropic.claude-code-2.1.141-win32-x64). Re-apply after upgrade or downgrade.
  • Encoding matters on Windows PowerShell 5.x. Set-Content defaults can corrupt the JS file by writing UTF-16 with BOM — always pass -Encoding UTF8.
  • Close VS Code before patching so the webview reloads with the new code.
nunawa · 2 months ago

@The-best-of-us
I think this issue has been resolved in the latest version 2.1.142. Have you tried upgrading?

bornsl0ppy · 2 months ago

V 2.1.142, bug still alive

SpencerBelleau · 2 months ago

Yep, seconding, I'm on 2.1.142 and still getting this.

kitbeaupre-boop · 2 months ago

also still getting this bug on 2.1.142

mundorfd · 2 months ago

Still reproducing on v2.1.141 (build .672), macOS, VS Code extension.

The Unhandled case: [object Object] · View output logs · Troubleshooting resources toast fired five times in one session today (2026-05-15), spanning roughly 90 minutes (09:12 → 10:43 PT). Local logs show each occurrence follows the same sequence:

  1. Stream starts: Stream started - received first chunk
  2. Stream stalls: [WARN] [Stall] stream_idle_partial lastChunkAgeMs=15000 bytesTotal=N idleDeadlineMs=300000 (warning repeats every 15s, bytesTotal stays flat)
  3. Stream ends abnormally: webview logs sdk_stream_ended_no_result with had_error: true
  4. Webview renders the generic Unhandled case: [object Object] toast — the actual error object is not surfaced

Timestamps from today's log (all v2.1.141.672):

| Time (PT) | had_error | message_count |
|---|---|---|
| 09:12:17 | true | 13 |
| 09:13:06 | true | 15 |
| 09:48:51 | true | 37 |
| 10:36:28 | true | 114 |
| 10:43:22 | true | 129 |

MCP servers configured: claude.ai Rube, claude.ai Google Drive (both connect cleanly at startup; claude.ai Gmail and claude.ai Google Calendar are skipped as needs-auth). The Rube transport stays connected — no explicit MCP disconnect is logged before the toast — but the user-facing fallout matches the original report: the webview can't decode the underlying error object and falls back to the generic string.

If the linked PR has shipped to build .672, I'm not seeing it. Happy to attach a redacted log slice around any of the five timestamps if that would help triage.

heidkaemper · 2 months ago

Seems to be working again with 2.1.143 on macOS for me so far.

Fanisimos · 2 months ago

+1, hitting this hard on Windows 11.

  • Extension version: 2.1.141.f34 (claude-vscode)
  • Model: Claude Opus 4.7 (1M context)
  • OS: Windows 11 Pro 26200
  • Symptoms: stream_idle_partial after ~670 bytes, then

sdk_stream_ended_no_result had_error: true

  • Session size when it triggered: ~480 messages
  • Cache hit rate in logs: cch=00000 (zero cache hits —

every retry reprocesses full context, expensive)

  • Webview shows literal "Unhandled case: [object Object]"

red banner, chat stops rendering

Tried: reload window, VPN off (never on), latest extension.
Nothing helps except starting a fresh session.

This is costing real money — partial streams still bill
input tokens and the cache misses compound it. Please
prioritize either (a) a retry button so we don't lose
the turn, or (b) suppressing billing when had_error=true
fires before any usable output.

Milupa5 · 2 months ago
+1, hitting this hard on Windows 11. * Extension version: 2.1.141.f34 (claude-vscode)

Yes it's there from version 140 to 142. we confirmed 139 does not have it and we are not sure about 143 but 1ppl said it is ok. You definitely use the bugged version.

UnravelSports · 1 month ago

Just reporting I'm still getting it on Mac OS too for 2.1.143.

romankanevsky · 1 month ago

Second repro on v2.1.143 (build .143). The render-miss class of this bug is still alive on 2.1.143 for me, not just the visible "Unhandled case" toast.

Environment

  • Extension: anthropic.claude-code-2.1.143 (Linux remote via VS Code Remote-SSH from Windows 11 laptop)
  • Spawn confirmed in extension log: Spawning Claude with SDK query function ... version: 2.1.143 ... permission mode: bypassPermissions
  • Model: claude-opus-4-7 1M context
  • Permission mode: bypassPermissions (so not the auto-mode toAutoClassifierInput workaround path mentioned in #58984)
  • Network path: laptop -> corporate Prisma Access / Zscaler -> api.anthropic.com

Repro this morning

Same shape as the session I described in #59054. User submitted a slash-skill invocation with ~3 KB prompt, model ran ~12 tool calls (Bash, Read, mcp__servicenow__*, ToolSearch), produced a 5253-char final assistant text block, all written to JSONL on disk.

GUI tab stayed truncated at the user prompt. Final text never appeared in the panel. Subsequent shorter assistant turns from outside the skill scope did render fine in the same session.

Stall signature on 2.1.143

2026-05-18T08:54:09Z  Stream started - received first chunk
2026-05-18T08:55:22Z  webview event log_event sdk_stream_*  (multiple tool turns OK)
2026-05-18T08:56:11Z  [WARN] Streaming stall detected: 45.8s gap between events (stall #1)
2026-05-18T08:56:39Z  Assistant text block (5253 chars) persisted to session JSONL

The 45.8 s stall lands inside the final-text streaming window, ending ~28 s before the JSONL write. Same network/timing profile as my earlier repro in #59054 (74.5 s + 36.2 s stalls in the same turn).

Differentiator I haven't seen explicitly called out yet

In my session JSONLs, every assistant entry that fails to render in the webview has the field attributionSkill: "<skill-name>" set. Every assistant entry that does render in the same session has no attributionSkill field (because it was generated outside any slash-skill scope). Sample shape of the not-rendered entry:

{
  "type": "assistant",
  "attributionSkill": "<skill-name>",
  "message": { "role": "assistant", "content": [{"type": "text", "text": "..."}], "stop_reason": "end_turn" },
  ...
}

It is the only structural difference between the missing-from-GUI entry and the visible entries in the same session. I have not been able to verify whether the webview switch has a missing branch for skill-attributed assistant turns (the webview/index.js bundle on 2.1.143 still has the QB1 assertNever pattern from the OP), but the correlation across two independent sessions on two different versions is exact.

Other observation from 2.1.143 log

Stop hook validation failure fires at the same moment the missing assistant turn is persisted, schema rejecting hookSpecificOutput.additionalContext on Stop events (only valid for PreToolUse/UserPromptSubmit/PostToolUse/PostToolBatch). Unlikely to be the cause of the render miss (the hook output is informational and the turn is already persisted by the time the validation runs), but the timing collision is consistent across both my sessions, in case it shares a code path with whatever wires the assistant turn into the webview.

Asks for maintainers

  1. Could you reopen, or open a new follow-up issue, to track the render-miss class specifically? "Unhandled case" toast and "skill-scoped assistant text not rendered after upstream stream stall" look like two outcomes of the same renderer fragility but only one of them is closed-as-fixed.
  2. Could webview/index.js switch table dump (case list) be made available so users can verify which streaming event types are exhaustively handled in the current build? That would let affected users self-diagnose whether their stalled-and-resumed event hit an unhandled case branch.

Happy to attach full JSONL + extension log for both sessions privately.

github-actions[bot] · 9 days ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.