[BUG] VS Code webview stuck on "Thinking" — sessionID discarded on restore (v2.1.141, regression of #35004)
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
In the Claude Code VS Code extension's webview chat panel, after sending a message the UI sometimes gets stuck on the "Thinking" indicator indefinitely — the streaming UI never transitions to showing the response, even though the response is generated normally on the backend. This is happening to me during ordinary use of the extension, not in any contrived setup. I have hit it organically multiple times; I was not reloading the window, restarting the extension, or doing anything unusual. The first couple of times I assumed the response was just slow, but it never arrived and the "Thinking" indicator stayed forever.
Closing the conversation tab and reopening it from the sessions list reveals the full response on disk: the CLI completed normally and the assistant turn was persisted to ~/.claude/projects/<encoded-cwd>/<sessionId>.jsonl, but the live webview UI never received the streaming updates and stayed on "Thinking". Only the webview-to-extension binding is broken; nothing else fails.
Workaround: Close the conversation tab and reopen it from the sessions list. Your reply MAY be there with no data loss. I've seen it end up with the session stuck forever (unrecoverable), and I've seen the session with all of the data and able to continue working.
The exact natural trigger isn't pinpointed — it's some webview-lifecycle event during or after streaming (visibility change, focus change, panel layout shift, or similar). Investigation of the bundled extension.js shows two specific call sites where the saved sessionID is discarded (U.setupPanel(w, void 0, void 0, I) in deserializeWebviewPanel, and this.getHtmlForWebview(z.webview, void 0, void 0, !0) in resolveWebviewView) — see Additional Information. Any lifecycle event that re-runs either of those handlers will leave the new webview with sessionId === undefined and unable to subscribe to the in-flight session.
This matches the long-running cluster around closed/locked issues #35004, #42504, and #35022 — the same minified void 0 argument-discard pattern documented there is present in v2.1.141 (and v2.1.140). It is distinct from open issue #45729 — see "Why this is NOT #45729" in Additional Information.
What Should Happen?
After the webview is re-deserialized (panel-tab restore) or re-resolved (sidebar view-provider lifecycle), it should restore its session binding from the saved state (or Memento / workspaceState) and continue subscribing to the in-flight session, rather than initializing with sessionId === undefined and orphaning the conversation.
Error Messages/Logs
Live capture from a controlled reproduction on Windows 11, v2.1.141 win32-x64.
Trigger sequence: open Claude Code panel webview, send a long-form prompt
("Generate a 1500-word essay on the lifecycle of honeybees..."), wait for
"Thinking" indicator to appear, then Command Palette -> "Developer: Reload
Window". After reload, the restored panel sits on "Thinking" indefinitely.
Extension Output log
(%APPDATA%/Code/logs/<timestamp>/window1/exthost/Anthropic.claude-code/Claude VSCode.log):
- Around the post-reload activation window, "Getting authentication status"
and "OAuth tokens found in secure storage" each fire FOUR times within
~700 ms. Consistent with the "case init" handler in extension.js running
for four webview surfaces on reload: claudeVSCodeSidebar primary, the
secondary-sidebar provider, claudeVSCodeSessionsList, and the
claudeVSCodePanel restore. So init succeeded on the extension side.
- After this burst, the file received no further entries despite the
webview UI remaining stuck on "Thinking" for minutes afterward.
The webview <-> extension message channel is dead while the extension
host itself is alive.
Renderer log (same logs root, /window1/renderer.log) and VS Code's own
Developer Tools (Help -> Toggle Developer Tools) console:
- "Extension host (LocalProcess pid: 7900) is unresponsive." appears
twice during the test window.
- "Extension host (LocalProcess pid: 7900) is responsive." appears
twice; each unresponsiveness window resolves.
- VS Code's UNRESPONSIVE-host profiler attributes both windows to
amazonwebservices.aws-toolkit-vscode (one example:
"amazonwebservices.aws-toolkit-vscode took 78.13% of 1366.638ms"),
NOT to anthropic.claude-code. Claude Code's extension host stays
healthy throughout the hang. This is the diagnostic shape that
distinguishes the bug from #45729, where the extension host itself
becomes unresponsive after Claude Code's init handler stalls.
Session JSONL on disk
(~/.claude/projects/<encoded-cwd>/<sessionId>.jsonl):
- Contains the user prompt entry at the time the message was sent,
plus the deferred-tool listing and skill-listing attachments that
Claude Code persists at session start.
- In this controlled reproduction, the JSONL does NOT contain a
completed assistant response, because "Developer: Reload Window"
terminates the extension host process and that termination
cascades to the CLI subprocess mid-stream. Under natural triggers
(the originally-experienced version of this bug, where a webview
lifecycle event destroys and recreates the webview without
killing the host), the CLI continues running and the JSONL is
fully populated by the time the user reopens the conversation
tab. The Ctrl+R reproduction is a stronger version of the same
underlying sessionID-discard defect; it also kills the CLI as
a side effect, but the webview's "stuck on Thinking" symptom is
identical in both cases.
Webview-side DevTools console (Developer: Open Webview Developer Tools)
was not captured for this report. Happy to capture and attach if helpful.
Steps to Reproduce
Naturally-occurring repro (how I actually hit this):
- Open VS Code with the Claude Code extension installed (v2.1.141 on
win32-x64). - Open Claude Code in panel webview mode (defaults:
claudeCode.preferredLocation: "panel",claudeCode.useTerminal: false). - Use Claude Code normally. Send prompts. Read responses.
- At some point, after sending a message, the "Thinking" indicator appears and never goes away. I have not pinpointed the exact trigger — I may have briefly changed focus to another editor, changed the panel layout, or otherwise caused a webview re-render during streaming. Nothing unusual. I have hit this organically more than once before investigating.
- Closing and reopening the conversation tab from the sessions list reveals the full response that the CLI generated and persisted to disk while the webview was stuck — confirming the disconnect is purely on the webview-to-extension binding.
The natural trigger is intermittent because it requires the webview to re-render during a streaming response, which is a small window. Once a webview lifecycle event fires in that window, the new webview is constructed with sessionId === undefined (see Root Cause in Additional Information) and cannot subscribe back to the still-running CLI session.
Deterministic forcing function (for verification by a maintainer):
If the natural occurrence is hard to catch live, the following sequence reliably reproduces the same symptom by deliberately re-running deserializeWebviewPanel while a response is in flight:
- Steps 1–3 above.
- Send a prompt that streams for ~10 seconds, e.g. "Generate a 1500-word essay on the lifecycle of honeybees, from egg through queen succession, including the role of pheromones. Write the full essay; do not summarize or outline first."
- While the "Thinking" indicator is visible, open the Command Palette (
Ctrl+Shift+P) and runDeveloper: Reload Window. - After the reload, the restored Claude Code panel re-renders with the "Thinking" indicator still visible and stays stuck on it indefinitely.
This forcing-function repro is a stronger version of the same defect: Developer: Reload Window also terminates the extension host process and therefore the CLI subprocess, so in this case the response is not persisted to disk before the hang. The webview's "stuck on Thinking" symptom and underlying sessionID-discard code path are identical to the natural-trigger case; only the CLI lifecycle differs.
Claude Model
None
Is this a regression?
Yes, this worked in a previous version
Last Working Version
2.1.83
Claude Code Version
2.1.141 (Claude Code)
Platform
Anthropic API
Operating System
Windows
Terminal/Shell
Other
Additional Information
Root cause — call-site evidence in v2.1.141 extension.js
The bug is at the call sites in deserializeWebviewPanel and resolveWebviewView. setupPanel's method definition itself is fine.
extension.js v2.1.141, SHA256 23F19A6044439E67C5F2532C3FD02BB63397EE7835040A51BA114474367ABD1E, 2,158,607 bytes.
Bad call site #1 — panel-tab restore (line 882 of extension.js):
window.registerWebviewPanelSerializer("claudeVSCodePanel", {
async deserializeWebviewPanel(w, R) {
let v = R, I;
if (typeof v?.isFullEditor === "boolean") I = v.isFullEditor;
else I = window.tabGroups.all.findIndex((_) => _.viewColumn === w.viewColumn) === 0;
U.setupPanel(w, void 0, void 0, I) // v?.sessionID is discarded
}
})
The saved-state object R is bound to v, its isFullEditor field is extracted correctly, but v?.sessionID is never passed to setupPanel. Variable-name map for v2.1.141: U is the manager class, w is the panel, R (= v) is the saved state, I is isFullEditor. Identical bug shape to #35004's version-by-version table, just with the v2.1.141 minifier-name rotation.
Suggested fix:
U.setupPanel(w, v?.sessionID, void 0, I)
Bad call site #2 — sidebar view provider (line 822 of extension.js, inside U.resolveWebviewView(z, K, N)):
resolveWebviewView(z, K, N) {
let V = { isVisible: () => z.visible };
this.webviews.add(V);
z.webview.options = { enableScripts: !0, localResourceRoots: [...] };
z.webview.html = this.getHtmlForWebview(z.webview, void 0, void 0, !0) // sessionID and initialPrompt = void 0
// ... constructs new m8(this.context, ..., void 0, ...) — that void 0 is `panelTab`, intentionally empty for sidebar
}
WebviewViewProvider.resolveWebviewView does not receive a saved-state argument like deserializeWebviewPanel does. The persisted sessionID could be retrieved from this.context.workspaceState / globalState / a Memento — but the current implementation reads no such state, so the sidebar webview always opens with sessionId === undefined.
(Note: the new m8(..., void 0, ...) in this same body is not a bug. Tracing class m8 extends Pb shows arg 15 is panelTab — legitimately empty for a sidebar webview that has no associated panel. Earlier #42504 commentary that called this slot initialSessionId was incorrect for v2.1.141's class shape.)
Good call site for contrast — createPanel (same line 822, ~5–6 KB earlier in the minified line):
let B = window.createWebviewPanel("claudeVSCodePanel", "Claude Code", x, { ... });
let O = N === ViewColumn.Active;
this.setupPanel(B, z, K, O) // real sessionID `z` passed correctly from createPanel's args
The setupPanel method definition itself (line 822 of extension.js):
setupPanel(z, K, N, V) {
// z = panel, K = sessionID, N = initialPrompt, V = isFullEditor
// ...
z.webview.html = this.getHtmlForWebview(z.webview, K, N, !1, V); // K propagated correctly
let H = new m8(this.context, O, this.settings, z.webview, ..., z, ..., !!V, ...);
// ...
}
setupPanel itself correctly propagates K (sessionID) into getHtmlForWebview and into the new m8(...) communicator. The bug is purely at the two call sites above where void 0 is passed instead of the saved sessionID.
Version map (abridged — full history in #35004)
| Version | setupPanel call inside deserializeWebviewPanel | State |
|---|---|---|
| v2.1.87 (per #35004) | Z.setupPanel(A, void 0, void 0, v) | Broken |
| v2.1.89 (per #42504) | regressed, same shape | Broken |
| v2.1.140 (this install) | minifier shape: ?.setupPanel(F, void 0, void 0, I) | Broken |
| v2.1.141 (this install) | U.setupPanel(w, void 0, void 0, I) | Broken |
Variable names rotate with each minifier run; the bug shape is identical across versions. The full progression from v2.1.74 onward is documented in #35004's comment thread.
Why this is NOT #45729
#45729 reports a different hang in the same general extension. That bug's hang is in the init request handler (line 282 of extension.js):
case "init": {
let N = this.getAuthStatus(),
V = z.channelId ? this.channels.get(z.channelId) : void 0;
if (this.cachedCurrentRepo === void 0) {
let B = await this.teleportService.detectCurrentRepository(); // can block indefinitely
if (B) { ... }
}
// ...
return { type: "init_response", state: { ... } }
}
If teleportService.detectCurrentRepository() hangs (git command stalls, network probe times out, WSL pipe hangs), the init response never returns. The webview sits forever waiting; the extension host gets marked unresponsive shortly after. That fits #45729's described symptom — blank webview, no further log lines after the init message arrives, host eventually marked unresponsive — before any conversation is possible.
This bug is different: init succeeds (a conversation is established, a message is sent, a response is generated and persisted). The hang fires later, when a webview lifecycle event re-deserializes the panel or re-resolves the sidebar view without restoring the session binding. The two code paths do not overlap.
Aside: the "AuthManager initialized" log appearing twice in #45729's broken trace is not a bug signal. Tracing the three new K2(...) construction sites in v2.1.141 (K2 is the AuthManager class; the log line is in its constructor) shows that the manager class plus the first m8 communicator each construct one K2 on activation. Two AuthManager-initialized lines is the steady-state baseline for a sidebar-only setup, three for a setup with both sidebar and a restored panel tab. It's not a defect.
Related cluster
- #35004 — analytical anchor; version-by-version variable-name table from v2.1.74 through v2.1.87 (closed/locked).
- #42504 — v2.1.89 regression report with video evidence; first to identify that both
deserializeWebviewPanelandresolveWebviewViewpaths are affected (closed/locked). - #35022 — Windows variant on v2.1.76; same root-cause family (closed/locked).
- #55453 — Cursor-specific renderer-hang variant; related-but-distinct (open).
- #45729 — distinct root cause (
teleportService.detectCurrentRepository()hang inside the init handler); see section above (open). - Excluded as different root causes: #56200 (
onWillSaveTextDocumentsettings-write hang, dup of #45729), #20960 (~/.claude/projects/.jsonlaccumulation → OOM at startup), #59090 (separate UI defect: thinking-process expander not clickable).
Environment
| Component | Value |
|---|---|
| OS | Windows 11 Pro 25H2 (OS build 26200.8457, UBR 8457) |
| VS Code | 1.120.0, commit 0958016b2af9f09bb4257e0df4a95e2f90590f9f, x64 |
| Node | v24.6.0 |
| Claude CLI | 2.1.141 |
| PowerShell | 7.6.1 |
| Extension | anthropic.claude-code v2.1.141, target platform win32-x64 |
| claudeCode.useTerminal | not set (default false → webview is in use) |
| claudeCode.preferredLocation | "panel" |
| claudeCode.allowDangerouslySkipPermissions | true |
| claudeCode.initialPermissionMode | "bypassPermissions" |
Binary hashes (to pin the bundle a maintainer is looking at):
| File | Size (bytes) | SHA256 |
|---|---|---|
| extension.js (v2.1.141) | 2,158,607 | 23F19A6044439E67C5F2532C3FD02BB63397EE7835040A51BA114474367ABD1E |
| webview/index.js (v2.1.141) | 4,798,391 | D756D1D369CFB41AD0EC620506C7C7E11B2FBF4528516B62E0375A5E658E3B13 |
| webview/index.css (v2.1.141) | 374,037 | E186CBA6F0E68E839C177ECE0879864531A536AED63AF5537BB753ECEBAA5F7D |
| extension.js (v2.1.140) | 2,154,411 | 2D82CDC5F638B42EE8FF2F3365F9AAFB3FA94FAA47C00E33C18D30FC6332E146 |
Attached files
vscode-console-log.txt— Renderer-process DevTools console capture (Help → Toggle Developer Tools→ Console tab), saved during the controlled reproduction. Contains theExtension host (LocalProcess pid: 7900) is unresponsive.andis responsive.markers, and VS Code's UNRESPONSIVE-host profiler attributing both unresponsiveness windows toamazonwebservices.aws-toolkit-vscode(one entry:took 78.13% of 1366.638ms) — not toanthropic.claude-code. This is the distinguishing signal from #45729: Claude Code's extension host stays healthy throughout the hang.live-hang-claude-vscode-log-500.txt— Last 500 lines of the Claude Code extension Output channel log (%APPDATA%/Code/logs/<timestamp>/window1/exthost/Anthropic.claude-code/Claude VSCode.log) from the same reproduction. Shows fourGetting authentication status/OAuth tokens found in secure storagepairs in quick succession during the post-reload activation window (init handlers running for all four webview surfaces — sidebar primary, sidebar secondary, sessions list, restored panel tab), then complete silence in the channel for minutes while the webview UI remains stuck on "Thinking". Confirms the webview ↔ extension message channel is dead even though the extension host itself remains alive.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗