[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)

Status Open
Reported on v2.1.226
Maintainer reply None cached
Activity 5 comments · opened Aug 8, 2026

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

  1. Open a conversation with a reasonable number of turns in the VSCode extension.
  2. Let it go fully idle - no generation in flight.
  3. Hover a previous user message → Fork conversation from here.
  4. A new tab opens, blank. The prompt you forked from is never submitted.
  5. Check the session list - the fork isn't there.
  6. Check ~/.claude/projects/<project>/ - the forked .jsonl exists 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-resume end 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 ENOENT on 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>.

View original on GitHub ↗

4 Comments

RobbiGarza · 21 days ago

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:

  • Extension: anthropic.claude-code 2.1.226
  • Claude Code CLI on PATH: 2.1.87
  • VS Code: 1.132.0 (arm64)
  • OS: macOS 26.6 (Darwin 25.6.0), arm64
BasedGPT · 21 days ago

The four forked .jsonl files 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-recovery for related VS Code session-list failures. Run python tools/diagnose.py first and follow the exact command it prints. If it routes you to recover_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 .jsonl and run claude --resume <that-uuid>. That confirms the transcript file is usable and keeps the extension's missing resume field separate from a real loss of conversation history.

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

alexklimin89 · 10 days ago

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:

09:46:51.817  fork_conversation      forkedFromSession: <SRC>, resumeSessionAt: <MSG>
09:46:52      <FORK>.jsonl written to disk — 68.1 MB, complete, session ids rewritten
09:46:52.701  new_conversation_tab   sessionId: <FORK>     <-- id IS present here
09:46:53.911  Spawning Claude ...    resume: undefined     <-- id is gone

For contrast, a normal session open in the same log a minute earlier:

launch_claude  ... "resume":"<SRC>","thinkingLevel":"default_on"

The launch_claude message the webview emits after a fork has no resume field at all. The extension host then forwards e.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 own launchClaude() call. From the shipped webview bundle:

startNewConversationTab(e,t) -> send({type:"new_conversation_tab", initialPrompt:e, sessionId:t})
launchClaude(e,t,i,n,o)      -> send({type:"launch_claude", channelId:e, cwd:i, resume:t, ...})

The id reaches the first call; the second is invoked with t undefined. 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_session firing immediately after the first prompt). An unopened fork therefore carries no custom-title / ai-title entry 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: cleanupPeriodDays defaults 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. Raising cleanupPeriodDays mitigates it, but the orphan shouldn't be created in the first place.

Happy to provide more log extracts if useful.

alexklimin89 · 10 days ago

Follow-up to my comment above — two findings since, one of which gives a working manual recovery for orphaned forks.

Correction: last-prompt is the visibility key, not the titles

I 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-prompt is appended each time a prompt is sent:

{"type":"last-prompt","lastPrompt":"...","leafUuid":"<uuid>","sessionId":"<id>"}

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 raw lastPrompt text). The decisive case was a session I created with a single "." as the prompt: it has last-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. Take leafUuid from the last user message in that file:

{"type":"last-prompt","lastPrompt":"<text of last user message>","leafUuid":"<its uuid>","sessionId":"<orphan uuid>"}
{"type":"custom-title","customTitle":"RECOVERED_<whatever>","sessionId":"<orphan uuid>"}

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 with os.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 cleanupPeriodDays default 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.

Showing cached comments. Read the full discussion on GitHub ↗