[BUG] Same session open in two VS Code windows forks the transcript and silently drops one branch from context
What's Wrong?
When the same session is open in two VS Code windows, both windows hold a live channel to
a different claude process for that session, and both append to the same transcript
JSONL. The conversation tree splits at a common parent. Because resume follows a single
leaf, one branch becomes unreachable — that part of the history silently disappears from
the conversation on the next resume, while remaining in the file.
This is a consequence of the same launch defect described in #83059 (a newclaude --resume process is spawned per session-tab activation, and the previous one is
never closed). I'm filing it separately because the severity is different: #83059 is
wasted memory, this is lost user data.
There is no file locking — lsof shows no process holds the JSONL open (open → append →
close). Nothing prevents the concurrent append. What normally prevents damage is only that
superseded processes lose their input channel and go silent; two windows defeat that,
because sessionPanels is per-window and neither window can see the other's processes.
Steps to Reproduce
Preconditions, which took some finding:
- Both windows' Claude panel
cwdmust match.listSessions()collects candidates
per-cwd, so a window whose panel cwd differs will not list the session at all.
- VS Code refuses to open the same folder in two windows.
- A Duplicate-Workspace command bypasses that. In the command palette, searching
duplicate surfaces a command that converts the folder to a multi-root .code-workspace
and opens it in a second window pointing at the same directory. That satisfies the cwd
condition.
Then:
- Create a throwaway session and send two messages ("1", "2"). Baseline: 1 root, 0 forks.
- From the window holding it, run the Duplicate-Workspace command.
- In the new window, open the Claude Code panel and select the same session:
```
11:04:46.661 [window6] launch_claude channelId=88woxwg1rs8 resume=7191659b…
11:04:46.709 [window6] Spawning Claude with SDK query function -> PID 52268
Closing Claude on channel` appears in the original window. Both windows now hold
No
live channels to different processes for the same session.
- Send "A" in window 2, then "B" in window 1.
Result
The JSONL forks at a common parent:
parent uuid=74cd6c86 (the tip after the "2" turn)
├─ 11:06:29 user "A" appended by PID 52268 (window 2)
└─ 11:06:35 user "B" appended by PID 41542 (window 1)
lines=32 malformed=0 duplicate-uuids=0 roots=1 fork-points=1
Neither process saw the other's turn. The reply to "B" had no knowledge of "A" and vice
versa — each held an independent in-memory context and appended to the same file.
In this run the later leaf was "B", so "A" and its response are dropped from context on the
next resume. The bytes are still in the file; the conversation can no longer reach them.
The file itself is not corrupted — no malformed lines, no duplicate UUIDs, a single root.
The damage is structural: the history has two mutually exclusive versions and only one
survives.
What this costs in practice
In a real session rather than a throwaway one, whichever branch loses is a set of decisions,
findings, and agreements that the session will not know about the next time it opens. The
agent then continues from a history that is missing work it actually did. We hit a
non-destructive version of this confusion during the investigation that led to #83059, and
it cost hours of misattribution — the failure mode is genuinely hard to notice from inside.
Environment
| | |
|---|---|
| Extension | anthropic.claude-code 2.1.220 (reproduced); 2.1.221 has byte-identical code on all relevant paths |
| VS Code | 1.131.0, commit e4c7e7b1d6d060162f4aa7f8225271b67ce1df75, arm64 |
| OS | macOS, Darwin 25.5.0, arm64 |
Suggested direction (untested)
Fixing #83059 alone does not fix this — that defect is per-window, and the two windows
never share a registry. Offered as a pointer only; I have not validated either option:
- Make the session→process registry cross-window.
~/.claude/sessions/<pid>.jsonalready
records sessionId per process, so the information needed to detect a second claimant
already exists on disk.
- Or take an advisory lock on the transcript so a second writer is detected and refused (or
the second window is put into a read-only/observer mode).
Either way it would also help to surface the condition to the user, rather than letting two
windows silently diverge.
Related
- #83059 — the underlying launch defect (new process per tab activation, previous never
closed). Root cause, reproduction, and corrected memory figures are in a comment there.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗