One evening I went to open a settings file I had just asked Claude to write, and I stopped. The reply said the file was created. The folder showed nothing, and ls in the terminal showed nothing either.
There was no error anywhere. That was exactly what made my stomach go a little cold. When a failure is visible, you simply redo it. The harder case is believing it succeeded and moving on to the next step.
I was in the middle of tidying store metadata for one of my wallpaper apps, as an indie developer, and asking Claude to write JSON into a folder is a routine part of that. Here is how I narrow down this kind of mismatch, and the small check I now put after every write.
Telling the symptoms apart: "not created" versus "not kept"
I find it easier to split the symptom in two.
| Symptom | Common cause | First thing to check |
|---|---|---|
| Reply says success, file exists somewhere else | The target path differs from what you expected | The absolute path in the reply, the working directory |
| Reply says success, file exists nowhere | The write never landed (permissions, a synced folder, connection state) | Reading the same path back (ENOENT or not) |
| Editing existing files works, only new files vanish | The create path behaves differently | Trying create and edit separately |
There is a community report of a filesystem MCP server where only new-file creation returned success without leaving a file. Editing and reading existing files stayed reliable, and relaunching the app did not change it. It was reported under a cloud-synced mount, which caught my attention.
I do not treat that report as a diagnosis. I read it as a clue that the same shape of symptom has been seen, and then I narrow things down in my own environment.
The order I narrow it down
Having a fixed order keeps me calm.
- Copy the path from the reply and pass it to
ls -lunchanged. - If it is missing, run
ls -laon the parent. Is the directory gone, or only the file? - Repeat the same operation in a folder that is not synced, for example under
/tmp. - Check whether appending to or editing an existing file works.
- Look at the connected tools and their status. If one has dropped, reconnect it.
Step 3 is the pivot. If it only happens in the synced folder, the suspects narrow from the write path to the sync layer. If it also happens locally, I treat it as a tool-side problem.
The fix: read it back right after writing
Before any diagnosis, I put one safeguard in place. If I wrote it, I read it. I look at the file itself, not at the wording of the reply.
This script checks that the expected file exists, is not empty, and contains what I expect. What it solves is simple: instead of trusting "created," it lets the real file decide whether the next step may run.
#!/usr/bin/env bash
# verify_written.sh — read-back check right after a write
# Usage: ./verify_written.sh <path> [expected string]
set -u
path="$1"
expect="${2:-}"
if [ ! -e "$path" ]; then
echo "NG: does not exist: $path" >&2
ls -la "$(dirname "$path")" >&2 2>/dev/null || echo "NG: parent directory not found either" >&2
exit 1
fi
if [ ! -s "$path" ]; then
echo "NG: file is empty: $path" >&2
exit 2
fi
if [ -n "$expect" ] && ! grep -qF -- "$expect" "$path"; then
echo "NG: expected content not found: $expect" >&2
exit 3
fi
echo "OK: $(wc -c < "$path") bytes — $path"I use three distinct exit codes so the caller can tell "missing," "empty," and "wrong content" apart. Even in an unattended run, the log shows where it stopped.
From Claude Code, calling it right after the write feels natural.
# Example: check right after asking for a settings file
./verify_written.sh ./config/app.settings.json '"version"' || echo "needs to be recreated"If you would rather embed it in Python:
from pathlib import Path
def verify_written(path: str, expect: str = "") -> None:
"""Check the file on disk. Raise if the write did not stick."""
p = Path(path)
if not p.exists():
raise FileNotFoundError(f"write did not persist: {p}")
text = p.read_text(encoding="utf-8")
if not text.strip():
raise ValueError(f"file is empty: {p}")
if expect and expect not in text:
raise ValueError(f"expected content missing: {expect!r}")
verify_written("./config/app.settings.json", '"version"')If it still does not work
When the read-back fails and recreating gives the same result, I try three things in order.
- Move the target outside the sync. Create the file locally, then copy it into the synced folder once it is complete. If that works, the sync layer becomes the likelier cause.
- Stop creating and start editing. Place an empty template by hand first, then let Claude fill it in by editing. It is a detour that follows the report's observation that editing stayed reliable.
- Write through another route. If the tool path is unstable, have Claude write with a shell command and run the same check.
None of these is a root-cause fix. Still, with the check in place, I notice before the work stalls.
The line I drew
Early on, I trusted replies too much. If Claude said "created," I went straight to the next step. The results were not great: a later process built on a file that did not exist surfaced as a different error, and tracing it back cost me extra time.
Now my rule is this: treat the words as a hint, and the file as the fact. Whatever the reply says, I only count a write as done once I have read it back.
If you want a starting point, pick one place in your current workflow where Claude creates a new file, and put verify_written.sh right after it. That is where I started, too.