[BUG] /resume shows no sessions when project directory is an NTFS junction (VS Code extension + CLI resolve different paths)
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?
When a project directory is an NTFS junction (e.g. C:\Users\<user>\Development\MyProject → D:\MyProject), the VS Code extension and the Claude CLI subprocess resolve the working directory to different paths. The extension resolves the real target path (D:\MyProject) and looks for sessions under ~/.claude/projects/D--MyProject/. The CLI subprocess does not follow the junction and stores sessions under ~/.claude/projects/C--Users-<user>-Development-MyProject/. Running /resume in the extension shows an empty session list even though sessions exist on disk. The same behaviour is observed on the conversations side panel.
What Should Happen?
/resume should list all prior sessions for the project regardless of whether the project directory is a junction/symlink. The extension and CLI should agree on which directory key to use for session storage.
Error Messages/Logs
(no error is shown — /resume silently displays an empty list)
Steps to Reproduce
- Create an NTFS junction for a project directory:
mklink /J C:\Users\<user>\Development\MyProject D:\MyProject
- Open
C:\Users\<user>\Development\MyProjectin VS Code with the Claude Code extension active. - Start a Claude Code session and have at least one conversation.
- In a new session, run /resume or look at conversation side panel.
- Observe: session list is empty.
Claude Model
Not sure / Multiple models
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.183
Platform
Anthropic API
Operating System
Windows
Terminal/Shell
VS Code integrated terminal
Additional Information
Root cause: VS Code's extension host calls fs.promises.realpath which follows NTFS junctions, resolving cwd to the junction target (D:\MyProject). The CLI subprocess uses realpathSync from a different Node.js runtime that does not follow junctions, so it stores sessions under the original path. The two processes key their session directories off different resolved paths and never see each other's data.
Workaround: Manually create NTFS junctions inside ~/.claude/projects/ mapping both resolved path names to the same underlying session directory, either direction.
Showing cached comments. Read the full discussion on GitHub ↗
7 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
The issue is not directly a duplicate of those, since it refers to junctions and not mapped drives like #633527 or #62913
It's also unrelated to #46342 because that one is about the claude binary itself not following symlinks on macos, and this is about a discrepancy between the vscode extension and the CLI on the name of the folder where the sessions are saved due to junction resolution on windows.
This looks like a project-slug mismatch rather than missing transcript files: the VS Code extension is searching
D--MyProjectwhile the CLI wrote sessions underC--Users-<user>-Development-MyProject. I builtBasedGPT/claude-code-session-recoveryfor this class of split. Runpython tools/diagnose.pyfirst; it will confirm which metadata files point at each slug and print the safe repair command for this diagnosis. Run the diagnosis before changing anything, then quit Claude Desktop fully before applying the printed repair. Do not remove the active junction while a session still has that path in itscwd.The repair should repoint the affected metadata to the project slug that contains the matching JSONL files, without moving the transcript files themselves. That should bring the sessions back under one project entry while preserving the underlying data.
Hope this helps, if my tools are able to help you, would appreciate a ⭐ :)
Confirming this on 2.1.220 (still present three releases later), with a third trigger: a volume that is mounted twice — once as a drive letter and once as a volume mount point. No
subst, no network drive, no reparse point on the project directory itself.Your root-cause analysis matches what I arrived at independently before finding this issue, including the swallowed
ENOENTand the "only the active conversation is listed" masking effect. Adding my data in case a second trigger helps confirm the scope.Setup
One physical volume, two mount points. This is deliberate and cannot be removed here: the whole dev team builds DevExpress components against a hardcoded
c:\dxpath, so the development volume must be reachable asC:\, while our directory layout refers to it asG:\develop\.So
G:\develop\main\akaraandC:\main\akaraare the same directory — same volume, two names.G:\developis a volume mount point, so the reparse point sits on an ancestor of the workspace folder, not on the workspace folder itself.Same divergence
GetFinalPathNameByHandleprefers the drive-letter mount point, so the canonical form is alwaysC:\main\akara, whichever name you start from.Measured writer/reader split before the workaround:
Three intact transcripts on disk (one of them 2.1 MB spanning two days), list permanently empty,
claude --resume <id>working the whole time — lookup by explicit id scans every project directory instead of the one derived from the cwd, which is what makes the failure look selective rather than like a listing bug.One data point for your suggested fix #1
You note that changing only the writer to the native resolver would orphan existing transcripts. This setup demonstrates that concretely, and rules out the obvious user-side workaround.
Opening the workspace by the canonical path (
C:\main\akara) makes writer and reader agree — and the list is still empty, because they now agree on a directory that has never been written to. The history stays under the old key:So "just use the canonical path" silently starts a fresh history rather than fixing anything, which is the same failure mode a writer-side-only fix would produce on every affected machine. The fallback you propose in #3 (retry with the unresolved key when the resolved one is missing) would cover both.
Workaround confirmed
Same junction, and it took effect immediately without restarting VS Code:
After it, both spellings of the workspace path share one store and all sessions list correctly. Worth noting the junction also works because
~/.claude/projectsis case-insensitive on Windows, so the reader'sC--main-akaraand a writer'sc--main-akaraland in the same place.Possibly the same defect
These look like the same writer/reader split with different ways of producing a second name for one directory — may be worth consolidating:
stale)Environment
anthropic.claude-code-2.1.220-win32-x64~/.claudeon a separate volumeThat third trigger is useful evidence of the same split: an ancestor volume mount point can produce different project slugs even when the workspace directory itself has no reparse point. Three intact JSONLs under
g--develop-main-akarawhile the reader usesC--main-akara, with explicit resume still working, points to a lookup mismatch rather than missing conversation data.I built
BasedGPT/claude-code-session-recoveryfor this path-split family. From the affected Windows machine, runpython tools/diagnose.pyfirst and keep the output. It should show which project slugs contain the transcript files and print the exact repair command for the diagnosis. Do not apply a write-bearing command blindly.Do not treat opening
C:\main\akaraas a repair: as you found, both sides can agree on a new empty key while the old transcripts remain under the other slug. Preserve both source directories, and close Claude Desktop, VS Code, and any active CLI writers before applying a printed repair.The
repoint_session_to_jsonl.pypath is the relevant recovery boundary here because it repoints metadata to the slug containing each transcript file rather than moving the.jsonlfiles. That preserves the intact transcripts while the upstream resolver mismatch is addressed.Hope this helps, if my tools are able to help you, would appreciate a ⭐ :)
Thanks for the confirmation, hopefully we can hear something back from the team. It's obviously a class of issues all under the same umbrella, and it seems to be biting more and more people as they increase adoption in more... Esoteric environments like ours.
Additional reproduction scenario: Windows volume mount point (not just NTFS junctions)
Same symptom via a different Windows path feature. In my setup,
D:is a volume mounted atC:\D-Data\(configured via Disk Management, not an NTFS junction created withmklink).Environment:
What I see in the extension Output log (
Claude Codechannel):The extension resolves the volume mount to its backing path and looks for sessions at:
But 73 sessions exist at:
The
mklink /Dsymlink workaround documented here works for this case too.This suggests the fix needs to cover not just NTFS junctions but any Windows path canonicalization that produces a different string for the same physical location (volume mount points,
\\?\prefixes, mixed case, etc.).