VS Code extension: conversation that entered a git worktree restores as an empty conversation after VS Code restart (restored tab loses its sessionID)

Status Open
Maintainer reply None cached
Activity 4 comments · opened Jul 31, 2026

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?

Close VS Code while a conversation that has entered a git worktree is open/working. On relaunch, VS Code restores the conversation tab, but the tab comes back as a brand-new empty conversation — no history, generic "Claude Code" title, no error. The original transcript is intact on disk the whole time, in the worktree-keyed project directory. Conversations that stayed in the main checkout restore fine in the same relaunch.

The failure is in the extension's tab-restore path, not the CLI resume path: the persisted webview state for the affected tab has lost its sessionID, so on restore the webview sends launch_claude with no resume parameter and the extension spawns a fresh session. (The CLI itself resumes worktree sessions by ID without problems — a third worktree conversation in the same relaunch was resumed by ID and recovered fully, see "Counter-example" below.)

Because the workspace's "Past conversations" list is keyed to the workspace folder's project dir, the orphaned worktree conversation doesn't appear there either (see #34437), so from inside the UI the conversation looks permanently lost.

Evidence (from an occurrence on 2026-07-30 → 2026-07-31)

Three conversations were in git worktrees when VS Code was closed at ~00:07 local:

| Conversation (worktree) | Session ID | Outcome on relaunch |
|---|---|---|
| contracts-section-spec | 9719e32b-… | restored empty (no resume sent) |
| iot-commands-arm-signatures | 3acda470-… | restored empty (no resume sent) |
| plan200-wave5 | e8529909-… | resumed correctly by ID |

All main-checkout conversations (6+) resumed correctly.

1. Transcripts intact on disk — the "lost" conversations still exist, sizeable, last written at VS Code close time (00:07):

~/.claude/projects/…-Milbank-Platform--claude-worktrees-contracts-section-spec/
    9719e32b-….jsonl   432 KB   mtime Jul 31 00:07  (ends with a "last-prompt" record)
~/.claude/projects/…-Milbank-Platform--claude-worktrees-iot-commands-arm-signatures/
    3acda470-….jsonl   1.7 MB   mtime Jul 31 00:07

No duplicate/empty .jsonl with the same IDs was created anywhere — this is purely a lost-reference bug, not transcript corruption.

2. Healthy tabs persist their session ID in the workspace state.vscdb (webview serializer state):

"extensionId":"Anthropic.claude-code","state":"{\"isFullEditor\":false,\"sessionID\":\"fb8463b2-…\"}"

Grepping the workspace's state.vscdb finds the session IDs of every conversation that restored correctly — and zero occurrences of 9719e32b or 3acda470. The affected tabs' persisted webview state had lost the sessionID before/at shutdown.

3. Extension log on relaunch (…/logs/20260731T065003/window1/exthost/Anthropic.claude-code/Claude VSCode.log) — restored tabs that kept their state relaunch with resume:

06:50:11 Received message from webview: {"type":"launch_claude","channelId":"…","cwd":"c:\\…\\Milbank Platform","resume":"bbcdaab7-…", …}
06:52:10 … "resume":"d82f6bc4-…" …
06:55:14 … "resume":"e8529909-…" …   ← worktree session, resumed fine

…while exactly two launches (matching the two lost worktree conversations) arrive with no resume at all, preceded by rename_tab back to the generic default title:

06:51:46 {"type":"request", …"type":"rename_tab","title":"Claude Code", …}
06:51:46 {"type":"launch_claude","channelId":"1x24b2djycnj","cwd":"c:\\…\\Milbank Platform","permissionMode":"auto","thinkingLevel":"default_on"}   ← no resume
06:51:59 {"type":"launch_claude","channelId":"mkw1sj9lblj", …}                                                                                      ← no resume

Neither lost session ID appears anywhere in the relaunch log as a resume target.

Counter-example / narrowing observation

Being in a worktree does not break restore by itself: e8529909 (worktree plan200-wave5, a 20 MB transcript dating from Jul 15 that has survived many restarts) resumed correctly in the same relaunch. The observable difference: the two conversations that lost their tab state had been idle for ~30–60 minutes before VS Code was closed (last transcript activity 23:07 / 23:34 local; close at 00:07), while the surviving worktree conversation was active in the final minutes before close. This suggests the webview's persisted state (or the extension's session bookkeeping for the tab) is cleared or overwritten at some point after the session goes idle in a worktree, so whatever is on disk at shutdown no longer contains the sessionID.

What Should Happen?

Any of:

  1. The restored tab keeps its sessionID and resumes the conversation (the CLI-side resume demonstrably works, even across worktree-keyed project dirs); or
  2. If the tab state is unusable, the extension recovers the mapping some other way (e.g. its own tab→session store, or matching live/most-recent sessions for the workspace incl. worktree-keyed project dirs); or
  3. At minimum, the tab restores to a "couldn't restore this conversation — find it in past conversations" state instead of silently opening a brand-new empty conversation, and worktree-session transcripts are findable from the workspace's Past Conversations list.

Actual

Tab silently restores as a fresh empty conversation. The original conversation is invisible in the workspace's Past Conversations (worktree-keyed project dir), so to the user it reads as total loss of a long-running task's history.

Error Messages/Logs

Steps to Reproduce

  1. In the VS Code extension, open a conversation in a git repo workspace and have it EnterWorktree (transcript relocates to ~/.claude/projects/<munged-repo>--claude-worktrees-<name>/).
  2. Let the conversation go idle for a while (the failures correlate with ≥ ~30 min idle; a busy-until-close worktree conversation survived).
  3. Close the VS Code window; reopen the same workspace.
  4. The restored tab shows an empty "Claude Code" conversation; extension log shows launch_claude without resume; workspace state.vscdb contains no webview state with that sessionID.

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

v2.1.220

Platform

Anthropic API

Operating System

Windows

Terminal/Shell

VS Code integrated terminal

Additional Information

_No response_

View original on GitHub ↗

3 Comments

mattsims · 26 days ago

Reproduced on macOS, v2.1.221 (original report: Windows, v2.1.220). Workspace is a repo root with 35 git worktrees under .claude/worktrees/.

Restore burst

launch_claude events from one window's Claude VSCode.log, restore burst 13:03:36 – 13:17:56:

total launch_claude : 10
with resume         : 2
without resume      : 8

The two carrying a resume target:

13:17:45  "cwd":"<repo>",  "resume":"1e5ed476-…"
13:17:50  "cwd":"<repo>",  "resume":"208b4bfc-…"

All 10 events had "cwd":"<repo>" — the repo root. None carried a worktree cwd, including for tabs that had been running in worktrees.

Session IDs are still present in state.vscdb

The original report found zero occurrences of the lost session IDs in the workspace state.vscdb. Here, most are still present. Grepping the workspace state.vscdb for webview serializer state finds entries for six worktree conversations, none of which appears as a resume target anywhere in the relaunch log:

sessionID\":\"01cabb89-…\"   (×5 occurrences)
sessionID\":\"b2b8a2ad-…\"   (×4)
sessionID\":\"790354b9-…\"   (×4)
sessionID\":\"4a7da477-…\"   (×3)
sessionID\":\"7bc8dde8-…\"   (×2)
sessionID\":\"52e4c21f-…\"   (×1)

Two further affected conversations, 19b517a5 and 44733947, have zero occurrences. So both states — ID retained, and ID absent — occur in the same relaunch.

Transcripts intact

Nothing is lost on disk. The affected transcripts are present in their worktree-keyed project dirs, 1.6 MB – 7.4 MB each, last written at window-close time.

One observation that may be a useful lead. 208b4bfc is the only resumed session whose real transcript lives in a worktree bucket (4.3 MB there), and it is also the only one of these ten sessions with any file under the workspace bucket: a 109-byte custom-title record. The other nine have no file in the workspace bucket at all. The remaining small files in the workspace bucket are all teleported-from records belonging to unrelated sessions.

Recovery

Resuming by ID from the worktree's own cwd recovers the conversation fully, as does File → Open Folder on the worktree — which also makes the workspace session list show it normally.

That points at the practical impact the report already describes: because the session list is keyed to the workspace folder's project dir (#34437), worktree conversations are not reachable from the UI, so an empty restored tab reads as permanent loss rather than as something recoverable.

mattsims · 26 days ago

Follow-up: there is a way to trigger the "empty conversation instead of a resume" symptom deterministically, without a restart. It may be a faster way to exercise the same code path.

Mechanism

The extension registers a URI handler at runtime via window.registerUriHandler (it is not declared in package.jsoncontributes.uriHandler is absent). One of its routes:

case "/open": {
  let session = params.get("session"), prompt = params.get("prompt");
  vscode.commands.executeCommand("claude-vscode.primaryEditor.open", session, prompt);
  return;
}

primaryEditor.open calls createPanel(sessionId, prompt, viewColumn). createPanel accepts no cwd — the resulting panel inherits the workspace folder of whichever window handles the URI. So if the handling window's workspace does not own the session's project dir, the session id cannot be resolved.

Repro

  1. Open a repo root as the VSCode workspace. The repo has at least one conversation that ran inside a git worktree (.claude/worktrees/<name>), so its transcript lives in the worktree-keyed project dir.
  2. With that root window focused, fire the deep link for that worktree session:
code --open-url "vscode://anthropic.claude-code/open?session=<worktree-session-id>"
  1. A panel opens as a new empty conversation. The extension log shows a launch with no resume:
14:39:09  {"type":"launch_claude","channelId":"uf3w273jwkk","cwd":"<repo>","permissionMode":"plan","thinkingLevel":"default_on"}

No error is surfaced in the UI.

Control

Same session id, but open the worktree as the workspace folder first, then fire the identical deep link:

code <repo>/.claude/worktrees/<name>
# wait for the window to register, then
code --open-url "vscode://anthropic.claude-code/open?session=<worktree-session-id>"
14:39:57  {"type":"launch_claude","channelId":"4x2c3iht5fk",
           "cwd":"<repo>/.claude/worktrees/ops-monitor-selfcheck-fix",
           "resume":"7bc8dde8-…","permissionMode":"plan"}
14:39:57  Spawning Claude with SDK query function - cwd: <repo>/.claude/worktrees/ops-monitor-selfcheck-fix

Resumes correctly with full history. Same session id, same command — only the handling window's workspace differs.

Scope of the claim

This reproduces the observable symptom from the original report — the extension opening an empty conversation instead of resuming, with launch_claude carrying no resume. Whether it shares a root cause with the restart-path failure I have not established; the restart case additionally loses the tab's worktree cwd, which this route never had. Offering it because it needs no restart and no idle period, so it may be useful for bisecting.

In both cases the transcript is untouched on disk (byte-identical size and mtime before and after, no stray file created in the workspace's project dir), consistent with this being a lost-reference bug rather than transcript corruption.

BasedGPT · 24 days ago

The transcript is still on disk here; the empty tab is being launched as a new session because the VS Code restore state has lost the sessionID or the worktree context. The two worktree JSONLs being present, plus launch_claude arriving without a resume parameter, separates this from a missing conversation history file.

I built BasedGPT/claude-code-session-recovery for related VS Code session-list failures. Run python tools/diagnose.py first and follow the exact command it prints. If it maps these worktree JSONLs as dropped VS Code entries, run the printed dry run of recover_vscode_sessions.py and keep VS Code fully closed before applying it. As an immediate check, open the affected worktree as the VS Code workspace and resume one of the session IDs directly; that tests whether the transcript and its pointer are still usable.

The extension still needs to preserve the tab's sessionID and worktree cwd together, or show a recovery message instead of silently opening a fresh session.

Hope this helps, if my tools are able to help you, would appreciate a ⭐ :)

Showing cached comments. Read the full discussion on GitHub ↗