[BUG] Write post-write verification false-positives on WSL2 / local ext4 — bogus "bytes on disk" (== len(path)+96, or 0) and false "silently truncated" for files that are correct

Open 💬 2 comments Opened Jun 29, 2026 by firezym

Preflight Checklist

  • [x] I have searched existing issues and this specific failure mode hasn't been reported
  • [x] This is a single bug report
  • [x] First seen on the stable release (2.1.181) and still reproduces after upgrading to 2.1.195 — not fixed

Summary

On WSL2 with the repo on a local ext4 disk (not a network drive, not a cloud-synced folder), the Write tool's post-write verification fires on essentially every write and returns:

Write verification failed: <path> is N bytes on disk, expected M. The filesystem may have silently truncated the write (network drive / cloud sync).

…but the file on disk is 100% correct. This is a false positive in the verification layer, not real truncation.

This is not fixed. It was first hit on 2.1.181 — the stable-channel release — and still reproduces after upgrading to 2.1.195 (a build ahead of the stable tag; 2.1.195 is not itself the stable channel). So the issue is present on stable and remains unresolved after upgrading.

The reported "N bytes on disk" is provably bogus: across many writes it is exactly len(absolute_path) + 96, a constant offset that is completely independent of the file's actual content (it tracks the path length, not the file). In a subset of cases it instead reports 0.

This is the inverse of the known Windows/Cowork corruption bugs (#64598, #53940): there the detector hides real corruption; here the detector invents corruption that doesn't exist. The error message even reuses the "network drive / cloud sync" wording from the 2.1.181 changelog entry ("Fixed Write/Edit producing 0-byte or truncated files on network drives and cloud-synced folders") — ironically, 2.1.181 is exactly where this false positive was first observed, so that fix did not address (and may be the source of) this behavior. It makes the error look like a regression of that fix even though the file is fine and the disk is local ext4.

Environment

  • Claude Code: first observed on 2.1.181 (stable-channel release); still reproduces on 2.1.195. 2.1.195 is ahead of the stable tag and is not the stable channel — i.e. the bug exists on stable and is still unfixed after upgrading.
  • Platform: Linux, kernel 6.1.0-32-cloud-amd64, WSL2 (Windows interop present — notification hooks invoke /mnt/c/Windows/System32/cmd.exe + PowerShell)
  • Filesystem: repo and ~/.claude are on the same local ext4 device (df -T -> ext4). No network mount, no OneDrive/Dropbox/iCloud, not under /mnt/c.
  • Not Cowork. Plain CLI, no sandbox/virtiofs mount.

Reproduction

  1. In Claude Code on WSL2 with the project on local ext4, use the Write tool to create any new file with a few lines of text (ASCII or multibyte — both reproduce).
  2. The tool returns the Write verification failed ... silently truncated (network drive / cloud sync) error.
  3. Verify out-of-band:
  • wc -c <path> -> equals the EXPECTED value M (file is correct)
  • cat <path> -> full content present, nothing truncated
  1. Compare the tool's reported N against len(absolute_path) + 96. They match exactly. (Or N == 0 in a subset of cases.)

100% trigger rate: in my run 6/6 writes raised the error; 6/6 files were correct on disk.

Evidence

expected always equals the real content size (wc -c). The reported "on disk" N never does — it equals len(path)+96:

| file (under a 39-char dir prefix) | real size (wc -c) | tool expected | tool "on disk" N | len(abs_path) | len(path)+96 |
|---|---|---|---|---|---|
| .write_probe_195.txt (CJK + em-dash) | 130 | 130 | 155 | 59 | 155 |
| .write_probe_ascii.txt (pure ASCII) | 57 | 57 | 157 | 61 | 157 |
| p1.txt | 2 | 2 | 141 | 45 | 141 |
| p2_longername_padding_xxxxx.txt | 2 | 2 | 166 | 70 | 166 |
| p3b.txt | 491 | 491 | 142 | 46 | 142 |
| p3.txt (1st attempt) | 491 | 491 | 0 | - | - |

Controlled variation proves the offset is on the path, not the content:

  • p1.txt (2 bytes) and p3b.txt (491 bytes) — content differs by 489 bytes, both report len(path)+96.
  • p1.txt vs p2_longername_padding_xxxxx.txtsame 2-byte content, paths differ by 25 chars -> reported N differs by exactly 25 (141 vs 166).

So N is derived from a path-containing string, not from reading the file. The +96 is a constant boilerplate offset; it looks like the verifier measures the length of a path-embedding string (e.g. the File created successfully at: <path> (file state is current ...) success message) instead of fs.statSync(path).size / a re-read of the bytes.

Data integrity / impact

  • No permanent data loss observed — all 6 files ended up correct on disk.
  • But the verifier is a broken detector in both directions: its byte count is bogus, so it also cannot detect a real truncation. It just emits a scary, misleading error on healthy writes.
  • The N == 0 cases coincide with a read-after-write visibility lag: immediately after Write returned, ls/wc showed the file missing/empty, then it appeared a moment later (an async "File created successfully" notification arrived late). Same family as #63797 ("Bash/Read results intermittently return empty then flush late on Linux"). The verifier likely reads the file back too eagerly, before the write is visible — and when it doesn't read 0, it falls back to measuring the wrong (path-derived) string.

Why this is distinct from existing issues

  • #64598 (Read cache masks on-disk corruption, Windows/Cowork) — opposite direction; that detector hides real corruption, this one fabricates fake corruption. Mine is plain WSL CLI, no Cowork.
  • #53940 / #66142 / #65229 / #67585 — real truncation/data loss on Windows + Cowork/OneDrive. Mine has no real corruption and no network/cloud FS.
  • #49805 (closed) — Write reports success but file missing on Windows. Mine reports failure on files that are correct.
  • #63797 / #36038 — Bash/Read empty-then-late-flush on Linux. Same underlying read-after-write lag that triggers my N == 0 cases, different surface (this report is about the Write verifier).
  • Possibly belongs to the master ticket #54891 family of "silent write/verify failure modes," but is not one of its listed splits (not O_TRUNC, UTF-8 boundary, CRLF, nor concurrency).

Suspected root cause

The post-write verification compares expected = Buffer.byteLength(content) against a value that is not the actual on-disk size:

  1. When the just-written file isn't yet visible (WSL2 read-after-write lag), the read-back returns 0.
  2. Otherwise the compared "size" is a path-derived string length (len(path)+96) — i.e. it measures the wrong object, most likely the success-message string (which embeds the absolute path) rather than fs.statSync(path).size.

Either way the comparison is structurally wrong and produces false truncation errors.

Suggested fix

  • Verify with fs.statSync(path).size (or re-read + hash) against Buffer.byteLength(content) — never against any string that embeds the path.
  • If the read-back can legitimately lag (WSL2 / 9p / drvfs), add a short bounded retry (re-stat after a few ms) before declaring truncation, instead of trusting a single eager read.
  • Only emit the "network drive / cloud sync" copy when the path is actually on such a mount.

Workaround (for other affected users)

Treat this Write verification failed ... silently truncated (network drive / cloud sync) error as a false positive on WSL2/local ext4. Do not rewrite the file (it's already correct). Confirm with wc -c / re-Read / import / pytest. If you must consume the file immediately after the Write, allow for a brief read-after-write delay (re-check once) rather than assuming corruption.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗