claude-code - 💡(How to fix) Fix [BUG] Edit tool progressively fails on WSL2 9p mounts (statx ENOENT on existing files)

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Root Cause

The failure chain has three links:

  1. libuv uses statx() for all stat operations on Linux (kernels >= 4.11). When Claude Code's Edit tool validates a file before writing, it ultimately calls statx() via Node.js's fs module.

  2. The 9p filesystem driver creates stale negative dentries in the VFS dcache. When statx() does a path lookup on 9p, the kernel's directory entry cache can record "this name does not exist" (a negative dentry) even for files that exist on the server side (Windows/NTFS). This is a known 9p client caching bug (microsoft/WSL#13105, WSL#8443).

  3. libuv's fallback doesn't trigger. libuv falls back from statx() to fstatat() only on ENOSYS (syscall not implemented). Since 9p returns ENOENT (file not found) — a legitimate-looking errno from a corrupted cache — libuv trusts it and reports the file as missing. No fallback fires.

The progressive degradation occurs because each failed statx() lookup returns the cached negative dentry without revalidating against the server, and new lookups on not-yet-cached paths can race with server-side state to create additional stale entries.

Fix Action

Workaround

Users can route edits through sed -i (Bash tool) or the Write tool, but this defeats the purpose of Edit's diff-based interface. We maintain a per-session decision tree and circuit breaker in project configuration rules (.claude/rules/environment-constraints.md) to route around Edit entirely once failures begin — infrastructure built to avoid a broken core tool.

Code Example

Session on WSL2 with workspace on 9p/drvfs mount:

1. Read file A          → succeeds
2. Edit file A          → succeeds
3. Read file B          → succeeds
4. Edit file B          → succeeds
5. (repeat for several files over 10-20 minutes of work)
6. Read file C          → succeeds
7. Edit file CENOENT: no such file or directory, statx
8. Read file C          → still succeeds
9. Edit file AENOENT (file A worked earlier)
10. All subsequent EditsENOENT regardless of prior Read state
RAW_BUFFERClick to expand / collapse

Bug description

The Edit tool fails with ENOENT: no such file or directory, statx on files that verifiably exist when the workspace is on a 9p/drvfs mount (WSL2 mounting a Windows drive). The failure is progressive within a session:

  1. Early in a session, Edit works on recently-Read files
  2. After several successful edits, Edit begins failing on files that Read can still access
  3. Once failures start, they spread — eventually Edit fails on every file, including files it previously edited successfully
  4. The Read tool continues to work on all affected files throughout

This is the same underlying issue as #28015 (closed for inactivity, not resolution). That report documented ghost file creation; this report adds the progressive session degradation pattern, which was not captured previously and may explain why static reproduction attempts appeared intermittent.

Root cause

The failure chain has three links:

  1. libuv uses statx() for all stat operations on Linux (kernels >= 4.11). When Claude Code's Edit tool validates a file before writing, it ultimately calls statx() via Node.js's fs module.

  2. The 9p filesystem driver creates stale negative dentries in the VFS dcache. When statx() does a path lookup on 9p, the kernel's directory entry cache can record "this name does not exist" (a negative dentry) even for files that exist on the server side (Windows/NTFS). This is a known 9p client caching bug (microsoft/WSL#13105, WSL#8443).

  3. libuv's fallback doesn't trigger. libuv falls back from statx() to fstatat() only on ENOSYS (syscall not implemented). Since 9p returns ENOENT (file not found) — a legitimate-looking errno from a corrupted cache — libuv trusts it and reports the file as missing. No fallback fires.

The progressive degradation occurs because each failed statx() lookup returns the cached negative dentry without revalidating against the server, and new lookups on not-yet-cached paths can race with server-side state to create additional stale entries.

Minimal reproduction sequence

Session on WSL2 with workspace on 9p/drvfs mount:

1. Read file A          → succeeds
2. Edit file A          → succeeds
3. Read file B          → succeeds
4. Edit file B          → succeeds
5. (repeat for several files over 10-20 minutes of work)
6. Read file C          → succeeds
7. Edit file C          → ENOENT: no such file or directory, statx
8. Read file C          → still succeeds
9. Edit file A          → ENOENT (file A worked earlier)
10. All subsequent Edits → ENOENT regardless of prior Read state

The progression timing varies but the pattern is consistent: Edit degrades monotonically within a session and does not recover.

Suggested fix

We believe this is a negative dentry caching issue in the 9p VFS client. Path-based stat calls hit stale cache; fd-based alternatives (open the file, then validate via the file descriptor) may bypass it. Detecting 9p/drvfs mounts at startup via statfs() and applying a compatible codepath on those mounts would match what other cross-platform tools do (e.g., git's fsmonitor disables features on network filesystems the same way).

Happy to provide more technical detail on the dcache mechanism if useful.

Workaround

Users can route edits through sed -i (Bash tool) or the Write tool, but this defeats the purpose of Edit's diff-based interface. We maintain a per-session decision tree and circuit breaker in project configuration rules (.claude/rules/environment-constraints.md) to route around Edit entirely once failures begin — infrastructure built to avoid a broken core tool.

Environment

  • Claude Code: v2.1.132
  • OS: Ubuntu 24.04.4 LTS on WSL2
  • Kernel: 6.6.87.2-microsoft-standard-WSL2
  • Node.js: v22.22.2
  • Mount: D:\ on /workspace type 9p (rw,noatime,cache=5,access=client,msize=65536,trans=fd)
  • Model: Claude Opus 4.6

Related

  • #28015 — Same root cause (statx + 9p dcache), documented ghost file creation, closed for inactivity
  • #52771 — Related WSL2/9p performance issue (startup stat blocking)
  • microsoft/WSL#13105 — Upstream kernel bug: 9p path operations return ENOENT on existing files
  • microsoft/WSL#8443 — Original report of 9p ENOENT coherence failures

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING