Windows: Write/Edit tools fail with spurious EEXIST on sandboxed workspace (bindflt/bfs mount) — temp file does not exist

Open 💬 0 comments Opened Jul 4, 2026 by tyrion47

Environment: Claude Code on Windows 11 Pro (10.0.26200). Workspace is a sandboxed mount at %LOCALAPPDATA%\.cache\work (cowork-style; ACLs show a CodexSandboxUsers group).

Symptom: Every Write and Edit tool call fails with:

EEXIST: file already exists, open '<target>.tmp.<pid>.<random-hex>'

Deterministic — 8/8 attempts in one session, across multiple directories.

Evidence the EEXIST is spurious:

  • The temp filename has a fresh random suffix each attempt; a real collision is impossible.
  • No *.tmp.* files exist anywhere in the affected directories (verified by glob).
  • Immediately after each failure, a plain write to the SAME path from a child process succeeds.

Runtime split (all tested in the same session, same directory):

| Route | Result |
|---|---|
| Node (harness Write/Edit, exclusive-create temp) | EEXIST (spurious) |
| git-bash (MSYS) mkdir | false "File exists" on nonexistent names |
| PowerShell New-Item -ItemType Directory | silent phantom: reports success, creates nothing |
| Python io.open("w"), os.open(O_CREAT\|O_EXCL), os.mkdir | all succeed |
| cmd.exe mkdir, bash cat >, PowerShell Set-Content / [IO.File]::WriteAllText | all succeed |

Note Python's O_EXCL succeeds — the filesystem's exclusive-create is fine; only the Node/libuv and MSYS code paths get intercepted.

Filter stack (fltmc filters, admin): only stock Microsoft drivers active — bindflt (1 instance), bfs (7), WdFilter, CldFlt, luafv; wcifs/UnionFS idle. The sandbox pair bindflt (path mapping) + bfs (brokered file ops) is the apparent interceptor.

Ruled out: added a temporary Windows Defender path exclusion for the workspace and retested — EEXIST persisted unchanged, ruling out WdFilter/AV entirely. No third-party AV or sync client involved; this narrows the cause to the sandbox virtualization layer itself.

Impact: every native file edit fails on such workspaces; users must fall back to shell/Python writes, losing atomic saves and post-write hook integration.

Suggested fix: when the atomic-save temp file's exclusive-create returns EEXIST, (a) retry once with a new random name, and if that also fails, (b) fall back to a direct non-exclusive write (CREATE_ALWAYS semantics) — optionally gated to detected sandbox/bindflt mounts.

View original on GitHub ↗