Local sessions list empty for workspaces on a mapped network drive (native realpath != session-write path on Windows)
Version: 2.1.210 (VSCode extension), Windows 11
Summary
When a VSCode workspace is opened on a mapped network (SMB) drive (e.g. Z:\ mapped to \\SERVER\share), the Local sessions tab always shows "No sessions yet", even though sessions exist and claude --resume from a terminal lists/loads them fine.
Root cause
Sessions are stored under ~/.claude/projects/<encoded-cwd>/. There's an inconsistency in how the cwd is resolved:
- Session writer / CLI: uses the raw cwd
Z:\→fs.realpathSync('Z:\\')returnsZ:\→ folderZ--. Sessions are written here. - Extension session lister (
listSessions→Gse): resolves the path withfs.realpathSync.native(), which on Windows resolves the mapped drive to its UNC target\\SERVER\share→ folder--SERVER-share. This folder doesn't exist, soreaddirthrows and the list is empty.
Reproduction of the divergence:
fs.realpathSync('Z:\\') // => 'Z:\\' (folder Z--)
fs.realpathSync.native('Z:\\') // => '\\\\SERVER\\share' (folder --SERVER-share)
Steps to reproduce
- Map a network share to a drive letter (
net use Z: \\SERVER\share). - Open
Z:\as a VSCode workspace, run a Claude Code session. - Session file is written under
~/.claude/projects/Z--/. - Open the Sessions panel → Local → "No sessions yet" (CLI
claude --resumefromZ:\shows it correctly).
Impact
All local session history is invisible in the extension UI for any workspace on a mapped network drive.
Suggested fix
Use the same path-resolution strategy for listing as for writing (avoid realpathSync.native for the projects-folder key, or apply the same resolution in both places).
Workaround
Junction the folder the lister expects to the real one:
mklink /J "%USERPROFILE%\.claude\projects\--SERVER-share" "%USERPROFILE%\.claude\projects\Z--"
3 Comments
Confirming this on 2.1.220 (VS Code extension, Windows 11) — still present. Two things worth adding, since I ran into it independently and dug through the current bundle.
1. It reproduces without any network share.
Every report on this so far involves SMB, which makes it look like a niche NAS problem. A plain
substdrive triggers the identical split, so this needs no server to test:Anything that makes
GetFinalPathNameByHandlereturn something other than what the JS resolver returns lands here — mapped drives,subst, and presumably junctioned workspace roots.2. In 2.1.220 the call is the async
fs.promises.realpath, notrealpathSync.native.The lister path is
listSessions()→Pme()→LIt()→DIt()→ife(), whereife()is:Same effect — Node's async
realpathisuv_fs_realpath→GetFinalPathNameByHandle— but the identifier changed, so grepping the current bundle forrealpathSync.nativefinds nothing. The writer side still resolves via the sync JSrealpathSync(inDme()), which is where the divergence comes from.Everything downstream of the
readdiris innocent: I replayed the_b→cfe→XRt/afechain against real transcripts, and every session passes the filters with correct titles. It fails purely onreaddirof a directory that was never created.Workaround caveat. The
mklink /Jworkaround in the issue body works on 2.1.220 — junctions for 7 projects, and the Local list populates correctly for them in the UI. One warning for anyone applying it: remove the junction withcmd /c rmdir <link>—Remove-Item -Recursein PowerShell 5.1 deletes through the junction and takes the real transcripts with it.Still true for 2.1.228, samme issue here.
Still present on VS Code extension 2.1.270 (win32-x64), Windows 11 Pro 10.0.26200. Adding details from a fresh investigation, since the issue is marked stale and #93342 was closed as a duplicate of this one.
Setup: every workspace has
H:\redigoas its first folder, whereH:is a mapped SMB drive (\\fileserver\vhosts). 431 transcripts (1.6 GB) live under~/.claude/projects/h--redigo/, all with"entrypoint":"claude-vscode"and"cwd":"h:\\redigo". The Sessions panel shows nothing for them; a workspace onC:lists fine.Where the split is in 2.1.270 (
extension.js, names as minified in that build):buildSessionList()→Lb$({dir: cwd})→TK0→CK0→z = await Gt(dir)whereGtisF6(await K8.realpath($))withK8 = require("fs/promises").YL(z)then probes onlyprojects/<key(z)>.Fb$→fs.realpathSync(path)(JS implementation).On this machine, in the extension host's Node:
So
YLgetsENOENTon the only directory it checks and returns[]. No error is logged; the extension log only shows therestore_declinedline followed by an empty list, exactly as described in #93342.The CLI side is not affected. Zero-cost check (pointing
ANTHROPIC_BASE_URLat a closed port and reading the SessionStart hook type in the debug log):claude -p x --continue --no-session-persistencerun fromH:\<project>resumes the latest transcript (SessionStart:resume) both with the npm CLI 2.1.272 and with the extension's bundledresources/native-binary/claude.exe2.1.270. So the same session store is readable by the CLI binaries; only the in-process lister of the extension host misses it.Side effect worth knowing: the panel still lists the claude.ai remote (Remote Control) mirror of each session, with the same title as the invisible local one. Clicking it triggers a teleport that fetches 0 events and creates an empty 97-byte local session (
{"type":"teleported-from",...,"messageCount":0}), so the user ends up with an empty tab named like the real conversation.Suggested fix is the same as the OP: derive the project key for listing with the same resolution used for writing (drive-letter path, no native realpath), or probe both keys.