[BUG] deserializeWebviewPanel still discards the session ID in 2.1.220 — restored editor tabs always open blank (refs #35004)
Summary
The webview panel serializer registered for claudeVSCodePanel restores the tab but never the conversation, because it discards the session ID. Every editor-area Claude tab comes back blank after a window reload, crash, or restart.
This was previously reported in #35004 (bug, has repro) and closed not planned by the stale bot on 2026-05-10 — then auto-locked with a message directing similar reports to a new issue, which is what this is. Related: #64756 (Remote-SSH reconnect, same root cause) and #60937 (feature framing), both also auto-closed as stale. None were closed on merit, and the behavior is unchanged in the current release.
Version: 2.1.220 (Anthropic.claude-code, win32-x64) — confirmed the latest published build at time of filing. VS Code on Windows 11.
Repro
- Open a Claude Code session in an editor tab (not the sidebar) and exchange a few messages.
- Reload the window (
Developer: Reload Window), or kill/restart VS Code to simulate a crash. - The tab is restored in the right position — and is empty. The prior conversation is not reattached.
Expected: the restored tab rehydrates the session it was showing. Actual: a fresh, empty panel.
Root cause
The extension declares onWebviewPanel:claudeVSCodePanel and registers a serializer, so VS Code correctly offers the persisted state back. But the handler reads only a layout flag and drops everything else (deminified from extension.js):
vscode.window.registerWebviewPanelSerializer("claudeVSCodePanel", {
async deserializeWebviewPanel(panel, state) {
let E;
if (typeof state?.isFullEditor === "boolean") E = state.isFullEditor;
else E = vscode.window.tabGroups.all.findIndex(g => g.viewColumn === panel.viewColumn) === 0;
manager.setupPanel(panel, void 0, void 0, E); // <-- session ID position is hard-coded undefined
}
});
The receiving side already supports what's needed — setupPanel(panel, sessionId, prompt, isFullEditor) forwards argument 2 straight into the webview HTML:
setupPanel(e, t, r, n) {
...
e.webview.html = this.getHtmlForWebview(e.webview, t, r, !1, n);
}
and the live path maintains the mapping (createPanel(sessionId, prompt, viewColumn) → this.sessionPanels: sessionId → panel). So the plumbing to boot a panel onto an existing session exists and is used; only the restore path declines to use it.
The other half is that nothing persists a session ID to begin with. In the webview bundle, state is written exactly once at startup, with a single key:
let api = acquireVsCodeApi();
let store = new StateStore(api);
store.update({ isFullEditor: window.IS_FULL_EDITOR });
So even a fixed deserializeWebviewPanel would have nothing to read. A complete fix needs both:
- webview — include the current session ID in the persisted state, and update it when the session ID is assigned or changes (e.g. on resume);
- extension — pass it through:
setupPanel(panel, state?.sessionId, void 0, E).
Why the existing escape hatch doesn't cover this
claude-vscode.reopenClosedSession (Ctrl/Cmd+Shift+T) is gated on when: ... && claude-vscode.lastClosedWasSession. That context key only exists in a still-running window, so after a crash or restart the binding is inert. It addresses accidentally closing a tab, not restoring after the process dies — which is the case where recovery matters most.
Impact
No data is lost — transcripts are appended to ~/.claude/projects/<project>/*.jsonl during the turn and remain resumable. But recovery is entirely manual: the user must find the session in the sidebar list or run claude --resume, per tab, and must first realize the restored tab is empty rather than genuinely new. With several sessions open, a single crash silently detaches all of them at once.
Suggested fix
Persist the session ID alongside isFullEditor and pass it through in deserializeWebviewPanel. If full rehydration is undesirable as a default (cost of resuming N sessions on startup), a lighter option that would still remove the sharp edge: have the restored panel display the session it was attached to with an explicit "Resume" affordance, rather than presenting an empty panel indistinguishable from a new one — or gate auto-rehydration behind a setting, since none of the current 14 configuration options affect this.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗