[BUG] "Fork Conversation from here" doesn't work
Status Open
Reported on v2.1.84
Maintainer reply None cached
Workaround ✓ Mentioned in thread ↓
Activity 16 comments · opened Mar 26, 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?
"Fork Conversation from here" option doesn't work with
- Visual Studio Code : Version: 1.107.1 (Universal)
- Claude Code for VS Code : Version 2.1.84
Rest of the functionality is working fine.
What Should Happen?
"Fork Conversation from here" option should ideally open another pane with appropriate chat history forked at the right place.
Error Messages/Logs
No Error logs seen in the UI
Steps to Reproduce
Claude Code for VS Code extension with above mentioned versions and try "Fork Conversation from here" option.
Claude Model
Opus
Is this a regression?
Yes, this worked in a previous version
Last Working Version
_No response_
Claude Code Version
2.1.84
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
VS Code integrated terminal
Additional Information
_No response_
16 Comments
Found 2 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Possible duplicate issues listed above also got closed without fix.
This has been broken for at least 2½ months.
Longer than that.
Should not be hard to fix either. A real bummer.
Here's another dupe:
https://github.com/anthropics/claude-code/issues/25085
this is fucking ridiculous
It has been working for me for at least a couple of weeks, but I guess that's not true for everyone?
Does forking the convo chat actually fork the context (previous messages) for you?
That's how Codex and GHCP harnesses work for me, but not Claude Code, and it's very frustrating.
/copydoesn't work properly either - only copies one line of the message, not the entire message, and doesn't work at all in the VS Extension (not even present in the slash commands), even though the extension is just the CLI in a wrapper AFAIK.Anthropic makes great LLM models, but very frustrating harnesses, especially the VS Code extension, and they just let users reports go stale and auto-close them despite lots of community vote behind ignored issues.
Yeah, Claude Code must be the worst harness, but when I ask to fork a conversation, it includes all the history minus the message I selected for forking... or a little earlier. Today I tested it and it subtracted both the message I forked from and the message before that one.
It's working half the time, that's the issue. It's also never obvious why it doesn't work. It's so annoying.
Been happening since forever too.
From my experience, the feature has two modes:
has not been working for me for weeks/months too
Still reproducing on Linux, Claude Code extension v2.1.183 (linux-x64), VS Code 1.125.0 — so this isn't macOS-only and isn't fixed as of 2.1.183.
One additional trigger condition worth noting: I hit this when forking mid-turn, i.e. invoking "Fork Conversation from here" while the assistant is still generating its response. The new pane opens and the last message text is populated, but the conversation history isn't carried into it.
This lines up with the root cause in #63831: the spawned process starts with
resume: undefinedbecause theresumeSessionAtmessage ID from thefork_conversationrequest isn't passed through — so the fork point is lost and the new session starts without history.Can reproduce, on windows. Just freezes any time I try to fork the convo.
Root cause for the silent-fork failure, traced through the bundle on
2.1.218–2.1.220(linux-x64). The fork itself works; the result is thrown away immediately afterwards.Chain
fork_conversationsucceeds. A complete transcript is written to<projectRoot>/<new-uuid>.jsonland file-history backups are hard-linked. In my case: 9,303 valid JSONL lines, 47.8 MB, zero parse errors.data-initial-session=<new id>and callsactivateSessionFromServer(initialSession, initialPrompt).list_sessions:``
js
``let r = (await n.listSessions()).sessions.find(l => l.id === e);
if (!r) return false;
list_sessions→LIt→PIt→cfe→afe.afereads only the first and last 64 KB (jd = 65536) of each.jsonland must derive a label from one of:customTitle/aiTitle(head or tail),lastPrompt/summary(tail), or a first user message (head, viatIt). Otherwise:``
js
``let f = l || Rc(i,"lastPrompt") || Rc(i,"summary") || u;
if (!f) return null; // session omitted from the list entirely
"isCompactSummary": true, "This session is being continued from a previous conversation…"), whichtItskips by design;attachmentrecords — no other user message;last-prompt/summary/custom-titlerecord is carried over (source session: 935last-promptrecords; fork: 0).afereturnsnull→ the session is not inlist_sessions→activateSessionFromServerreturnsfalse→ the webview falls back tocreateSession({isExplicit:false})with only the prompt prefilled.Observable signature
The forked tab spawns without a
resumeargument — that is the tell, and it matches #31831 exactly:A normal resume carries
"resume":"<session-id>".This is not a mid-generation race. The same
resume: undefinedappears on forks taken while idle, and the missing-label mechanism also explains a symptom a race cannot: on my machine 13 unrelated large sessions (18–48 MB) were simultaneously invisible in the session picker, for exactly the same reason. Waiting for generation to finish is not a reliable workaround.Trigger condition
Forking any session whose transcript begins with a compact-summary — i.e. any conversation that has been auto-compacted at least once. Short sessions fork fine, which is why this looks intermittent.
Verification
Replaying
afe()'s logic over 48 session files reproduced the exact split: the 3 forked sessions plus 10 other large ones yielded no label; the other 35 did.Fix
A last-resort label makes
afetotal, so forked sessions always resolve:Patching that one expression in
extension.jsfixed forking here immediately. A cleaner upstream fix would be to haveforkSessionwrite acustom-title(or copy the source'slastPrompt) into the new transcript, sinceensureSessionLoadedalready understands{"type":"custom-title", "sessionId":…, "customTitle":…}.Recovering sessions already lost to this
Appending that record — the same one "Rename session" writes — makes an invisible session listable again without touching existing data:
Separate bug, same button
Forking from a message inside a Task/subagent block fails differently, with a visible error:
Subagent messages live in
<projectRoot>/<sessionId>/subagents/agent-*.jsonlwith"isSidechain": true, butensureSessionLoadedreads only<projectRoot>/<sessionId>.jsonl, soforkSessionthrows. Workaround: fork from a main-thread message.Root cause for the silent-fork failure, traced through the bundle on
2.1.218–2.1.220(linux-x64). The fork itself works; the result is thrown away immediately afterwards.Chain
fork_conversationsucceeds. A complete transcript is written to<projectRoot>/<new-uuid>.jsonland file-history backups are hard-linked. In my case: 9,303 valid JSONL lines, 47.8 MB, zero parse errors.data-initial-session=<new id>and callsactivateSessionFromServer(initialSession, initialPrompt).list_sessions:``
js
``let r = (await n.listSessions()).sessions.find(l => l.id === e);
if (!r) return false;
list_sessions→LIt→PIt→cfe→afe.afereads only the first and last 64 KB (jd = 65536) of each.jsonland must derive a label from one of:customTitle/aiTitle(head or tail),lastPrompt/summary(tail), or a first user message (head, viatIt). Otherwise:``
js
``let f = l || Rc(i,"lastPrompt") || Rc(i,"summary") || u;
if (!f) return null; // session omitted from the list entirely
"isCompactSummary": true, "This session is being continued from a previous conversation…"), whichtItskips by design;attachmentrecords — no other user message;last-prompt/summary/custom-titlerecord is carried over (source session: 935last-promptrecords; fork: 0).afereturnsnull→ the session is not inlist_sessions→activateSessionFromServerreturnsfalse→ the webview falls back tocreateSession({isExplicit:false})with only the prompt prefilled.Observable signature
The forked tab spawns without a
resumeargument — that is the tell, and it matches #31831 exactly:A normal resume carries
"resume":"<session-id>".This is not a mid-generation race. The same
resume: undefinedappears on forks taken while idle, and the missing-label mechanism also explains a symptom a race cannot: on my machine 13 unrelated large sessions (18–48 MB) were simultaneously invisible in the session picker, for exactly the same reason. Waiting for generation to finish is not a reliable workaround.Trigger condition
Forking any session whose transcript begins with a compact-summary — i.e. any conversation that has been auto-compacted at least once. Short sessions fork fine, which is why this looks intermittent.
Verification
Replaying
afe()'s logic over 48 session files reproduced the exact split: the 3 forked sessions plus 10 other large ones yielded no label; the other 35 did.Fix
A last-resort label makes
afetotal, so forked sessions always resolve:Patching that one expression in
extension.jsfixed forking here immediately. A cleaner upstream fix would be to haveforkSessionwrite acustom-title(or copy the source'slastPrompt) into the new transcript, sinceensureSessionLoadedalready understands{"type":"custom-title", "sessionId":…, "customTitle":…}.Recovering sessions already lost to this
Appending that record — the same one "Rename session" writes — makes an invisible session listable again without touching existing data:
Separate bug, same button
Forking from a message inside a Task/subagent block fails differently, with a visible error:
Subagent messages live in
<projectRoot>/<sessionId>/subagents/agent-*.jsonlwith"isSidechain": true, butensureSessionLoadedreads only<projectRoot>/<sessionId>.jsonl, soforkSessionthrows. Workaround: fork from a main-thread message.