[BUG] VSCode: forking copies the conversation but never attaches the new tab to it - blank chat, and the fork is invisible in the session list (2.1.226)
What's new here
The same missing-resume behaviour as #31831, but:
- Extension 2.1.226 - #31831 was 2.1.71, and its duplicate chain (#22016, #23692) is closed.
- Triggered while completely idle, not mid-generation. #31831 was closed as a race-condition duplicate; that framing doesn't cover this.
- 4/4 reproducible inside 70 seconds.
- The forked conversations survive on disk and are fully recoverable - shown below, with the recovery method.
- It explains #23692 (forks missing from the resume list) as the same root cause rather than a separate cosmetic bug.
Summary
Forking does two things: it copies the conversation up to the chosen message, then attaches a new tab to that copy.
The copy works. The attach doesn't.
The extension host writes a correct forked transcript - <new-uuid>.jsonl, truncated at the chosen message, full history intact. It then opens a new tab and launches Claude without a resume field, so a blank session starts instead of loading the copy.
The result: your fork is orphaned on disk, the tab in front of you is empty, and nothing indicates either happened.
The same failure produces a second symptom. Because the copy is never opened, nothing ever writes a last-prompt / ai-title / custom-title entry into it. The session picker keys off those, so the fork is also invisible in the session list. You are left believing the fork silently did nothing, when a complete forked conversation is sitting in your projects directory.
Environment
- Claude Code VSCode extension: 2.1.226 (2.1.224 also present in the extensions dir)
- Claude Code CLI on PATH: 2.1.222
- VS Code: 1.132.0 (x64)
- OS: Windows 11 Pro 26200
- Auth: OAuth · Permission mode: auto
- Not a git repo; workspace is on a OneDrive-synced path
Steps to reproduce
- Open a conversation with a reasonable number of turns in the VSCode extension.
- Let it go fully idle - no generation in flight.
- Hover a previous user message → Fork conversation from here.
- A new tab opens, blank. The prompt you forked from is never submitted.
- Check the session list - the fork isn't there.
- Check
~/.claude/projects/<project>/- the forked.jsonlexists and is complete.
Evidence: the attach never receives the session id
Four fork attempts within ~70 seconds, all against the same source conversation. From the extension log:
11:09:51.038 Received message from webview: {"type":"request","request":{"type":"fork_conversation",
"forkedFromSession":"<SRC>","resumeSessionAt":"<MSG-A>"}}
11:09:51.057 Received message from webview: {"type":"request","request":{"type":"new_conversation_tab",
"initialPrompt":"<redacted>","sessionId":"<FORK-1>"}}
11:09:51.463 Received message from webview: {"type":"launch_claude","channelId":"y1l5xapkqzq",
"cwd":"<redacted>","permissionMode":"auto","thinkingLevel":"default_on"}
11:09:51.468 Launching Claude on channel: y1l5xapkqzq
launch_claude carries no resume and no sessionId. <FORK-1> was known one line earlier - it is simply not forwarded to the launch.
For contrast, an ordinary resume from the session list, 20 seconds later in the same window:
11:10:11.384 {"type":"launch_claude","channelId":"t7lb7xb2s9","cwd":"<redacted>",
"resume":"<SOME-SESSION>","permissionMode":"auto","thinkingLevel":"default_on"}
Across every log on this machine: 71 launch_claude events, 38 carry resume. All four fork-tab launches are among the 33 that don't.
Evidence: the copies are intact
All four forked transcripts exist and are complete:
| fork | entries | human turns | forked at |
|---|---|---|---|
| FORK-1 | 215 | 14 | MSG-A |
| FORK-2 | 215 | 14 | MSG-A |
| FORK-3 | 215 | 14 | MSG-A |
| FORK-4 | 172 | 13 | MSG-B |
Each begins with the original opening prompt and carries the full history through to the fork point. Source conversation: 425 entries, 15 turns.
None contains a custom-title, ai-title, or last-prompt entry - which is why none appeared in the session list. Appending a single {"type":"custom-title",...} line to each made all four visible immediately after a window reload, and each resumes correctly via claude --resume <fork-id>.
So no data is lost. Only the attach fails.
Expected behaviour
The tab opened by a fork should launch with resume: <forked-session-id>, display the copied history, and submit the forked-from prompt.
Related issues (all closed)
- #31831 - same missing-
resumeend state and the same diagnosis, on 2.1.71, triggered by forking while the model is generating. Closed as a duplicate. This report is the idle-state case on a current version. - #22016 - Windows, "fork does nothing", 2.1.25. Root cause was
ENOENTon the projects dir, so the copy was never created. Here the copy is created correctly. - #36435 / #39484 - "context menu options do nothing", no log-level diagnosis.
- #23692 - forked sessions missing from the resume list, closed as not planned. The evidence above suggests this is the downstream effect of the failed attach rather than a separate listing bug.
Workaround
The fork is real. Ignore the blank tab, reload the window, and open the fork from the session list - that path sends resume correctly. If the fork isn't listed, find the newest .jsonl in ~/.claude/projects/<project>/ and run claude --resume <that-uuid>.
Showing cached comments. Read the full discussion on GitHub ↗
4 Comments
Confirming the same behavior here. Clicking the fork icon (curved back-arrow) on a past message opens a new window, but that window is a completely fresh session with none of the original conversation's context — matches the "copy works, attach doesn't" diagnosis above. Reproduced across multiple different sessions, not isolated to one conversation.
Environment:
anthropic.claude-code2.1.226The four forked
.jsonlfiles are already there; the blank tab is the attach step failing, not the conversation copy. Opening the fork from the session list after a reload, or resuming its UUID directly, should let you use the existing conversation history while the extension fix is pending.I built
BasedGPT/claude-code-session-recoveryfor related VS Code session-list failures. Runpython tools/diagnose.pyfirst and follow the exact command it prints. If it routes you torecover_vscode_sessions.py, run the dry run to see what it would add, then keep VS Code fully closed before applying anything.For one immediate check, look in
~/.claude/projects/<project>/for the newest fork.jsonland runclaude --resume <that-uuid>. That confirms the transcript file is usable and keeps the extension's missingresumefield separate from a real loss of conversation history.Hope this helps, if my tools are able to help you, would appreciate a ⭐ :)
Confirming on 2.1.237 (Cursor, macOS 26.6 arm64, extension host log). I captured the exact moment the session id is lost, which I think narrows this from "forking is broken" to a single hop.
The id is present, then dropped 1.2 s later
Fork performed while completely idle; source is a long-running conversation. Timings from
Claude VSCode.log:For contrast, a normal session open in the same log a minute earlier:
The
launch_claudemessage the webview emits after a fork has noresumefield at all. The extension host then forwardse.resume(undefined) into the spawn, so the blank session is a faithful consequence of an empty argument rather than anything going wrong in the host.So the loss is between
new_conversation_tab— which does receive the new session id — and the newly created tab's ownlaunchClaude()call. From the shipped webview bundle:The id reaches the first call; the second is invoked with
tundefined. Reproduced 2/2 today, and 23 times total since 2026-07-31.Minor refinement to the "invisible in the session list" symptom
The report attributes the fork's absence from the session picker to the same root cause. I think that part is simpler: a session is only titled after its first user message (the log shows
generate_session_title->rename_sessionfiring immediately after the first prompt). An unopened fork therefore carries nocustom-title/ai-titleentry for the same ordinary reason any untouched new chat doesn't. It's a consequence of the fork never being opened, not a second defect — though it is what makes the orphan unrecoverable through the UI.Scale, and a data-loss risk worth flagging
23 orphaned forks accumulated here between 2026-07-31 and 2026-08-20, ~0.93 GB, all from one conversation. Each is complete and opens fine with
claude --resume <uuid>.The part that concerns me most:
cleanupPeriodDaysdefaults to 30 days and deletes transcripts. An orphaned fork is written once and never touched again, so its mtime never advances — it is a prime cleanup candidate while being invisible in the UI for its entire life. A user who forks a conversation they care about can lose that copy silently, without ever having known it existed. RaisingcleanupPeriodDaysmitigates it, but the orphan shouldn't be created in the first place.Happy to provide more log extracts if useful.
Follow-up to my comment above — two findings since, one of which gives a working manual recovery for orphaned forks.
Correction:
last-promptis the visibility key, not the titlesI attributed the fork's absence from the session list to the missing title entries. More precisely, it's
last-prompt. Across 45 transcripts here:| |
last-prompt|ai-title|custom-title||---|---|---|---|
| sessions visible in the sidebar (19) | 19 | 6 | 15 |
| orphaned forks (25) | 0 | 0 | 0 |
last-promptis appended each time a prompt is sent:Its presence is what makes a session listable; the title entries only determine what the row is named (
custom-title>ai-title> falls back to the rawlastPrompttext). The decisive case was a session I created with a single "." as the prompt: it haslast-prompt, no title entry at all, and is listed — displaying "." as its name.So an orphan is unreachable for the plain reason that no prompt was ever sent into it. It needs a message to become visible, and visibility to receive a message.
Manual recovery (verified working)
An orphan can be made visible and fully usable by appending two lines to its
.jsonl. TakeleafUuidfrom the last user message in that file:Then
Developer: Reload Window. Result on a 68 MB orphan here: it appeared in the sidebar under the given name, opened with full history, and answered a context-dependent question correctly. It is now a normal working session. Append-only, so it reverts byte-exactly withos.truncate(path, original_size).claude --resume <uuid>also opens an orphan, but this brings it back into the extension UI, which is where users will look for it.Restating the cleanup risk, now that recovery is known to work
Since orphans are fully recoverable, the
cleanupPeriodDaysdefault of 30 days is destroying something genuinely retrievable. An orphan's mtime never advances past its creation, so it is the first thing a time-based cleanup removes, while being invisible in the UI for its entire life. Users lose recoverable forks without ever knowing they existed.Correction to my earlier figure: the final count here was 25 orphans, not 23 — two more from a second conversation that my initial content-based filter missed. Structural test (no
last-prompt, never modified after creation) is the reliable way to enumerate them.