[Windows] Background session cwd loses one leading backslash on UNC paths -> "working directory no longer exists or is not accessible"
Environment
- Claude Code 2.1.220 (CLI)
- Windows 11 Pro 10.0.26200
- Node v24.14.0
- Working directory reached through a UNC path (
\\RaiDrive-USER\Synology\dev\repo-a). Same share is also mapped toS:, so the identical folder is reachable under two spellings.
Summary
When a session's working directory is a UNC path, Claude Code stores it with one leading backslash instead of two and then rejects it as missing:
working directory no longer exists or is not accessible: \RaiDrive-USER\Synology\dev\repo-a
The path it was given is valid; the path it saved is not. \\server\share\... is a UNC path, \server\share\... is a drive-relative path, so the check can never succeed.
Evidence: the persisted value
~/.claude/jobs/<id>/state.json, verbatim (JSON \\ = one literal backslash):
{
"state": "failed",
"detail": "working directory no longer exists or is not accessible: \\RaiDrive-USER\\Synology\\dev\\repo-a",
"intent": "raidrive test",
"cwd": "\\RaiDrive-USER\\Synology\\dev\\repo-a",
"originCwd": "\\RaiDrive-USER\\Synology\\dev\\repo-a",
"template": "claude",
"backend": "daemon"
}
Both cwd and originCwd hold the single-backslash form, so the value is already mangled by the time it is recorded.
Evidence: Claude Code receives the correct path
A child process launched from that same directory inherits the correct two-backslash cwd, so nothing upstream is dropping it:
# terminal cwd = \\RaiDrive-USER\Synology\dev\repo-a
> node -e "console.log(process.cwd())"
\\RaiDrive-USER\Synology\dev\repo-a # correct, two backslashes
# terminal cwd = S:\dev\repo-a (same folder, drive-letter spelling)
> node -e "console.log(process.cwd())"
S:\dev\repo-a
And the mangled form genuinely does not exist:
\\RaiDrive-USER\Synology\dev\repo-a -> exists
\RaiDrive-USER\Synology\dev\repo-a -> does not exist
Evidence: same folder, two spellings, opposite outcomes
Every background session I ran today, from ~/.claude/jobs/*/state.json. Note rows 2 and 5 — the same directory, differing only in spelling:
| time | intent | state | stored cwd |
|---|---|---|---|
| 20:51 | TEST | failed | \RaiDrive-USER\Synology\dev\repo-a |
| 21:53 | test | done | S:\dev\repo-b |
| 21:54 | | done | S:\dev\repo-b |
| 21:58 | test | failed | \RaiDrive-USER\Synology\dev\repo-a |
| 22:09 | test4 | failed | \RaiDrive-USER\Synology\dev\repo-b |
| 22:11 | | running | S:\dev\repo-c |
| 21:06 | git pull | running | D:\dev\repo-d |
7 sessions started from a UNC cwd → all failed, all stored with one backslash.
4 sessions started from a drive-letter cwd (S: network drive or D: local) → all fine.
The network share itself is not the problem: S:\dev\repo-b works while \\RaiDrive-USER\Synology\dev\repo-b fails, and those are the same directory on the same share.
Likely cause
Node's path helpers preserve the UNC prefix; a separator-dedupe step does not:
const unc = '\\\\RaiDrive-USER\\Synology\\dev\\repo-a' // \\RaiDrive-USER\Synology\dev\repo-a
path.win32.normalize(unc) // \\RaiDrive-USER\Synology\dev\repo-a ok
path.posix.normalize(unc) // \\RaiDrive-USER\Synology\dev\repo-a ok
path.normalize(unc) // \\RaiDrive-USER\Synology\dev\repo-a ok
unc.replace(/\\+/g, '\\') // \RaiDrive-USER\Synology\dev\repo-a <-- matches the stored value
So this looks like a custom "collapse repeated separators" / path-join step rather than a stdlib call. Whatever normalizes the cwd before it reaches state.json needs to leave a leading \\ (or //) intact.
Steps to reproduce
- On Windows, have a directory reachable via UNC (
\\server\share\project) — a mapped network drive or any SMB share. - Open a shell whose cwd is the UNC form (
cd \\server\share\project;node -e "console.log(process.cwd())"should print two leading backslashes). - Start a background session there.
- It fails with
working directory no longer exists or is not accessible: \server\share\project, and~/.claude/jobs/<id>/state.jsonshowscwdwith a single leading backslash. - Repeat from
X:\project(the same folder via a mapped drive letter) — works.
Suggested fix
Preserve the UNC prefix when normalizing a working directory. A guard on the dedupe step is enough:
// keep exactly two leading separators for UNC paths
const isUnc = /^[\\/]{2}[^\\/]/.test(p)
Alternatively, use path.win32.normalize on Windows, which already handles this.
Related
- #76205 documents a different symptom on the same class of drives (empty
/resumehistory from a project-slug mismatch, caused by nativerealpathresolving mapped drives to UNC). I checked that mechanism here —fs.realpathSync.native('S:\\dev\\repo-a')does return\\RaiDrive-USER\Synology\dev\repo-aon this machine — but it is not what drives this bug: sessions started fromS:\...storeS:\...and work fine, so the stored cwd is the raw inherited cwd, not a realpath of it. The two issues are independent. - #69902 asks for graceful recovery when the working directory is gone; here the directory is present and the recorded path is simply wrong.
Happy to provide more state.json samples or test a patch.