Skip to content

fix(filesystem): verify write_file output landed on disk (#4138)#4274

Open
0ywfe wants to merge 1 commit into
modelcontextprotocol:mainfrom
0ywfe:fix/4138-write-file-post-write-verification
Open

fix(filesystem): verify write_file output landed on disk (#4138)#4274
0ywfe wants to merge 1 commit into
modelcontextprotocol:mainfrom
0ywfe:fix/4138-write-file-post-write-verification

Conversation

@0ywfe
Copy link
Copy Markdown

@0ywfe 0ywfe commented Jun 1, 2026

Summary

Closes #4138write_file in the filesystem server silently returned success on Windows even though no bytes reached disk. Callers received "File created successfully" with no way to detect the failure.

This change adds a post-write verification step inside writeFileContent:

  1. fs.stat the target after the write completes.
  2. Compare on-disk size to Buffer.byteLength(content, 'utf-8').
  3. Throw an explicit error on miss / mismatch.

The fix runs for both write paths (wx fresh-create and EEXIST atomic-rename) and converts the silent failure into a useful error message.

Why this approach

The host-side root cause varies by environment (the reporter ruled out NTFS permissions, Defender, Controlled Folder Access, OneDrive, paths-with-spaces — and reproduced on both isUsingBuiltInNodeForMcp: true and system Node). What's consistent is the failure mode: fs.writeFile resolves with no error and nothing is on disk.

Rather than chase the host layer, this closes the gap at the boundary we control. The verification is platform-agnostic — silent success is the wrong contract on any OS — so the same guard also catches partial writes, antivirus quarantine mid-write, and rename-to-wrong-path bugs.

Test plan

  • npm test — all 150 existing tests pass.
  • New tests in src/filesystem/__tests__/lib.test.ts (`writeFileContent` block):
    • Existing happy-path test extended to mock fs.stat and assert it is consulted.
    • Regression for [filesystem] write_file silently returns success but never writes to disk on Windows #4138: mocks fs.writeFile to resolve and fs.stat to reject with ENOENT, asserts a clear "reported success but the file is missing on disk" error.
    • Size mismatch: mocks fs.stat to report 0 bytes for non-empty content, asserts size-mismatch error.
    • Multibyte UTF-8 sanity: confirms Buffer.byteLength('héllo 🌍', 'utf-8') is the right yardstick.
  • npm run build — TypeScript clean.

Out of scope

  • Pinpointing the underlying Windows-host cause. That is worth a separate investigation; this PR is the boundary fix that makes the failure detectable today.
  • Applying the same verification to edit_file / applyFileEdits. The reporter confirms edit_file works correctly on the same paths, so it is not affected by the same failure mode. If desired in a follow-up, the same fs.stat-after-write check could be extracted into a helper.

Files

  • src/filesystem/lib.ts (+22)
  • src/filesystem/__tests__/lib.test.ts (+37, -2)

…tprotocol#4138)

write_file in the filesystem server resolved successfully even when no
bytes reached disk on Windows. Callers (Claude Desktop and any other MCP
client) received "File created successfully" and a normal structured
result, with no way to detect that the file was never written.

Reported in modelcontextprotocol#4138 against Windows 11 with both isUsingBuiltInNodeForMcp
and system Node. Repro ruled out NTFS permissions, Defender, Controlled
Folder Access, OneDrive, and paths-with-spaces. The same paths work for
edit_file, read_file, and list_directory; only write_file silently
no-ops.

The host-side root cause varies by environment and isn't fully nailed
down, but the failure mode is consistent: fs.writeFile resolves with no
error and nothing is on disk afterward. Rather than chase the host
layer, this change closes the gap at the boundary we control by adding
a post-write verification step to writeFileContent:

  1. fs.stat the target file after the write completes.
  2. Compare on-disk size to Buffer.byteLength(content, 'utf-8').
  3. Throw an explicit error on miss / mismatch.

This converts the silent-success failure mode into a clear "write
reported success but the file is missing on disk" error that callers
can surface to the user instead of returning a false-positive success.
The verification runs for both the fresh-write (wx flag) and atomic-
rename (EEXIST) branches.

The fix is platform-agnostic: silent success is a bad failure mode on
any OS, and the same verification protects against partial writes,
antivirus quarantine mid-write, and rename-to-wrong-path bugs in
addition to the original Windows symptom.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[filesystem] write_file silently returns success but never writes to disk on Windows

1 participant