[BUG] /resume shows no sessions when project directory is an NTFS junction (VS Code extension + CLI resolve different paths)

Status Open
Reported on v2.1.183
Maintainer reply None cached
Activity 8 comments · opened Jun 25, 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?

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\MyProject in 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.

View original on GitHub ↗

7 Comments

github-actions[bot] · 3 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/63527
  2. https://github.com/anthropics/claude-code/issues/62913
  3. https://github.com/anthropics/claude-code/issues/46342

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

cafedetal · 3 months ago

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.

BasedGPT · 2 months ago

This looks like a project-slug mismatch rather than missing transcript files: the VS Code extension is searching D--MyProject while the CLI wrote sessions under C--Users-<user>-Development-MyProject. I built BasedGPT/claude-code-session-recovery for this class of split. Run python tools/diagnose.py first; 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 its cwd.

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 ⭐ :)

gushifloyy · 2 months ago

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 ENOENT and 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:\dx path, so the development volume must be reachable as C:\, while our directory layout refers to it as G:\develop\.

> mountvol
    \\?\Volume{19bafb18-...}\
        C:\
        G:\develop\

So G:\develop\main\akara and C:\main\akara are the same directory — same volume, two names. G:\develop is a volume mount point, so the reparse point sits on an ancestor of the workspace folder, not on the workspace folder itself.

Same divergence

fs.realpathSync('g:\\develop\\main\\akara')           // 'g:\develop\main\akara'   <- writer
fs.realpathSync.native('g:\\develop\\main\\akara')    // 'C:\main\akara'
await fs.promises.realpath('g:\\develop\\main\\akara')// 'C:\main\akara'           <- reader

GetFinalPathNameByHandle prefers the drive-letter mount point, so the canonical form is always C:\main\akara, whichever name you start from.

Measured writer/reader split before the workaround:

cwd: g:\develop\main\akara
  writes to g--develop-main-akara -> 3 transcripts
  reads from C--main-akara        -> <ENOENT>

cwd: G:\develop\main\akara
  writes to G--develop-main-akara -> 3 transcripts
  reads from C--main-akara        -> <ENOENT>

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:

cwd: c:\main\akara
  writes to c--main-akara -> <ENOENT>
  reads from C--main-akara -> <ENOENT>
  agree: yes, and both empty

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:

mklink /J "%USERPROFILE%\.claude\projects\C--main-akara" "%USERPROFILE%\.claude\projects\g--develop-main-akara"

After it, both spellings of the workspace path share one store and all sessions list correctly. Worth noting the junction also works because ~/.claude/projects is case-insensitive on Windows, so the reader's C--main-akara and a writer's c--main-akara land 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:

  • #71224 — NTFS junction on the project directory (labelled stale)
  • #76205, #78461, #63527, #74656 — mapped network drives
  • #82787 — UNC-path workspace

Environment

  • Claude Code 2.1.220, VS Code 1.131.0, extension anthropic.claude-code-2.1.220-win32-x64
  • Windows 11 Pro 10.0.28000, ReFS, ~/.claude on a separate volume
BasedGPT · 2 months ago

That 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-akara while the reader uses C--main-akara, with explicit resume still working, points to a lookup mismatch rather than missing conversation data.

I built BasedGPT/claude-code-session-recovery for this path-split family. From the affected Windows machine, run python tools/diagnose.py first 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\akara as 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.py path is the relevant recovery boundary here because it repoints metadata to the slug containing each transcript file rather than moving the .jsonl files. 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 ⭐ :)

cafedetal · 2 months ago
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.

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.

wzr · 1 month ago

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 at C:\D-Data\ (configured via Disk Management, not an NTFS junction created with mklink).

Environment:

  • OS: Windows 11 Enterprise
  • VS Code: 1.131
  • Claude Code extension: 2.1.259

What I see in the extension Output log (Claude Code channel):

[Model][openRepository] Opened repository (path): d:\CustomTools\dev\claude
[Model][openRepository] Opened repository (real path): C:\D-Data\CustomTools\dev\claude

The extension resolves the volume mount to its backing path and looks for sessions at:

~/.claude/projects/C--D-Data-CustomTools-dev-claude/   ← does not exist

But 73 sessions exist at:

~/.claude/projects/d--CustomTools-dev-claude/   ← CLI finds these fine

The mklink /D symlink 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.).

Showing cached comments. Read the full discussion on GitHub ↗