VS Code/Cursor extension: session hangs forever after auto-mode classifier_request_finished (no timeout, webview channel errors silenced)
Summary
In the VS Code / Cursor extension, sessions freeze mid-run several times per session (4-6x). The hang happens in the auto-mode permission path: the permission classifier request completes successfully, but the decision never reaches dispatch — no tool_dispatch_start, no permission prompt, no error, and no timeout, so the session waits forever. The UI shows nothing; the only recovery is closing and reopening the chat (which rebuilds the webview channel) and sending a new message. Because the stall usually outlives the prompt-cache TTL, each recovery re-sends the full context uncached (~14% of a session's usage burned per stall).
Environment
- Extension: Claude Code for VS Code,
cc_version=2.1.220.26d,cc_entrypoint=claude-vscode - IDE: Cursor on macOS (Darwin 25.5.0)
- Permission mode: auto (Bash commands routed through the
claude-sonnet-5[1m]classifier,stage=xml_s1) - Plan: paid subscription
Evidence from Claude VSCode.log
Healthy flow (2026-07-26 17:08:29 UTC) — classifier finishes, decision lands, tool dispatches:
17:08:25.993 [INFO] [Stall] classifier_request_started reqId=37a6e8cd-... tool=Bash model=claude-sonnet-5[1m] stage=xml_s1
17:08:29.564 [INFO] [Stall] classifier_request_finished reqId=37a6e8cd-... outcome=ok durationMs=3571
17:08:29.581 [INFO] Slow permission decision: 3645ms for Bash (mode=auto, behavior=allow)
17:08:29.591 [INFO] [Stall] tool_dispatch_start tool=Bash toolUseId=toolu_01BQWDA1PsLZBiXdYSL2YRtr permissionDecisionMs=3645
Hang, 3 minutes later (2026-07-26 17:11 UTC) — same session, classifier finishes, then nothing, forever:
17:11:25.885 [INFO] [Stall] classifier_request_started reqId=1f0bae18-4ea6-47d5-a9f5-d2f0980acf8c tool=Bash model=claude-sonnet-5[1m] stage=xml_s1
17:11:25.955 [DEBUG] [API REQUEST] /v1/messages x-client-request-id=23678dae-fd3f-4140-beb1-28d7558d1888 source=side_query
17:11:28.436 [INFO] [Stall] classifier_request_finished reqId=1f0bae18-4ea6-47d5-a9f5-d2f0980acf8c tool=Bash stage=xml_s1 outcome=ok durationMs=2551
<-- nothing after this: no permission decision, no tool_dispatch_start, no error, no timeout -->
17:14:58.521 [DEBUG] Invalidating session environment cache <-- user closed/reopened the chat to recover
The command being classified was npx vitest ... (an allow-suggestion for it was logged at 17:11:25.829).
Likely related — every session start logs a webview channel error and then goes silent about further drops, which would explain why the hangs leave no error trace:
[WARN] claude-vscode notification channel error: Received a response for an unknown message ID: {"jsonrpc":"2.0","result":{},"id":0}; further channel errors are not logged this session
A second symptom of the same channel flakiness observed the same evening (17:28:16 UTC): the webview flipped the session to state:"idle" while a subagent (source=agent:builtin:general-purpose) was still actively streaming API requests.
Separate but related: silent give-up on 429
Two other freezes were a different failure: on an account rate limit the extension logs API error (attempt 1/11): 429 rate_limit_error but gives up immediately after attempt 1 (API rate_limit after retries) and flips the session to idle with no visible error. Request IDs for server-side lookup:
req_011CdPNNLX3yrcUAWzXCkjYN(2026-07-25 19:59 UTC+2)req_011CdQt9Dup3M2oLnovPihZ1(2026-07-26 15:10 UTC)- x-client-request-id
4be74d05-690b-46c0-b5e1-d5a84c4b05bf
Expected behavior
- A timeout + retry (or a surfaced error) on the permission-decision path so a dropped channel message cannot hang the session forever.
- Channel errors should keep being logged (or re-establish the channel) instead of "further channel errors are not logged this session".
- A 429 with retry budget "1/11" should actually retry with backoff, or clearly tell the user why it stopped.
Impact
4-6 hard freezes per working session; each recovery requires closing/reopening the chat and re-sending context uncached, burning a significant share of the session's usage quota.
3 Comments
Hitting the same "Secondary Issue: Silent Rate Limit Failures" pattern described above, with one additional data point that might help narrow down the root cause: the classifier's own system prompt appears to be the thing burning the rate-limit budget, not the main loop.
Environment
Evidence: classifier prompt size vs. main request
Claude Code dumps the failing classifier request to
/tmp/claude-*/auto-mode-classifier-errors/<sessionId>.txton error. For a single trivial action ({"Bash":"cd ~/workspace/<project> && ls -la"}), the dump showed:The classifier's own request (~28k tokens, system prompt alone is ~112k chars — a "security monitor for autonomous AI coding agents" prompt) is almost twice the size of the main-loop request it's evaluating. In an auto-mode session with many quick sequential tool calls (in my case amplified by a PreToolUse hook that intercepts every Bash invocation individually), this multiplies token throughput fast enough to trip a short-term rate limit even though overall plan usage is low.
Logs (matches the pattern already reported above)
Sample request_ids for backend correlation (repeated ~every 8-30s over multiple 5-10 minute windows, both before and after the version upgrade):
req_011CdvjCxJPVuxcSTfyj5R21(2026-08-11T09:31:28.414Z, v2.1.227)req_011CdvjCAu8udXCvDzXoFLFK(2026-08-11T09:31:17.770Z, v2.1.227)req_011CdvhSEEy1girrtvGf5LHX(2026-08-11T09:08:14.812Z, v2.1.152)req_011CdvhRySE8JvWajjmEuYws(2026-08-11T09:08:11.485Z, v2.1.152)Checked status.claude.com at the time: no ongoing incident reported.
Suggested fix direction
Given the classifier prompt is bigger than the request it's classifying, trimming what gets sent to the classifier (e.g. not re-sending full transcript/tool schemas on every single tool call, or caching the static parts) seems like it would both reduce latency (the 8-10s stalls) and make auto mode much less likely to trip short-term rate limits during normal fast-paced sessions — independent of fixing the retry/timeout behavior already requested above.
Happy to share the full dumped classifier-error file (redacted) if useful.
Hello — same webview channel error signature, different repro path, cross-linking for visibility.
We hit this exact log line too, but the trigger was different: a third-party
Anthropic-compatible provider emitting one SSE content_block_delta event
per token (thinking-heavy turns → ~6300 events per response). The extension
fell behind the stream (per-event render over Remote-SSH), the CLI blocked
on stdout backpressure, and permission/plan prompts were displayed
minutes-to-hours late — while our Notification hook fired on time, proving
the CLI had already processed the response.
Key data from the affected session:
6400 output tokens → 6314 SSE delta events (~135B each); the official API
batches multiple tokens per delta — 1–2 orders of magnitude fewer events.
Combination matrix: terminal + provider fine, extension + official fine,
extension + provider stalls — only the event granularity varies.
A proxy-level SSE coalescing workaround (merge consecutive same-type
deltas into time-windowed batches) cut the event rate ~3–23x; the prompt
delay went tens of minutes → ~3 min → seconds, with no other variable
changed.
Full writeup with the evidence: #86854.
Both cases point at the same CLI↔webview channel dropping/silencing
responses under stress — in yours the decision never dispatched with no
timeout; in ours responses arrived after the CLI had already dropped the
request. Worth fixing together: batch stream-event rendering on the webview
side, and stop silencing channel errors.
Still reproduces on 2.1.259 (VS Code extension native binary, macOS 26 / Darwin 25.6.0, Apple Silicon), permission mode
auto, with the sessions driven from another device via Remote Control. Two independent sessions on the same machine hung this way on the same day (11:22 and 16:53 local), while three sibling sessions under the same extension host kept working. Adding the process-level evidence since the earlier reports are mostly log-based.Fingerprint (same as above)
Bashtool_use; notool_result, and none of the internalatis-latch/frame-link/bridge-sessionentries that appear betweentool_useand result in every healthy round of the same transcripts.PreToolUseand oneUserPromptSubmithook are configured; neither was alive).S, 0.0% CPU, hours at a time. Not spinning.pmset -gshowssleep 0, uptime 17 days, no sleep/wake entries inpmset -g logaround either hang.What's different from a plain idle event loop: leaked half-closed sockets
lsof -ion the two hung processes vs. the healthy ones at the same moment:| session | API connections | Remote Control relay connection |
|---|---|---|
| hung A (stuck ~6 h) |
CLOSED|CLOSED|| hung B (stuck ~40 min) | 3×
CLOSE_WAIT, 1×CLOSED, 1×ESTABLISHED|CLOSED|| 3 healthy siblings | all
ESTABLISHED|ESTABLISHED|CLOSE_WAITmeans the far end closed the connection and the client never closed its side, i.e. a request whose connection was dropped without the client noticing or timing out. That matches the "classifier/permission path with no timeout" theory in this issue and the dropped-continuation shape in #94252 and #95354.Other observations
/tmp/cc-socks/<pid>.sock) refused messages from a peer session, consistent with the process being stuck inside a tool round.SIGTERMwas ignored;SIGKILLwas needed.claude --resume <id> --remote-control <name> --permission-mode autofrom a terminal on the machine picks the transcript up and re-issues the stalled call. Nothing can be done from the remote device, which is what makes this painful when the person is away from the machine.Happy to share transcript tails privately (they contain project paths).