[FEATURE] Restore opt-in strict read-before-overwrite for Write tool (data-loss footgun since v2.1.228)
Preflight Checklist
- [x] I have searched existing requests and this feature hasn't been requested yet
- [x] This is a single feature request (not multiple features)
Problem Statement
Since v2.1.228, Claude 5 family models may use Write to overwrite an existing file they never read in the conversation, as long as reading it wouldn't need a permission prompt (documented in the tools reference, "Write tool behavior"). Older models (Haiku 4.5, Opus 4.6 and earlier) still always refuse with "File has not been read yet."
This relaxation caused real data loss for me, and there is currently no way to opt back into the strict behavior. The lost content was only recoverable because session A's transcript still existed. Still filing it as a Feature Request instead of a Bug because it is the documented behavior so it seems to work as designed. But it's unexpected and leads to data loss.
Reproduction
Claude Code v2.1.238, macOS (darwin 24.6.0):
mkdir repro && cd repro
echo "ORIGINAL" > notes.md
# Older model: guard fires, file protected
claude -p --model haiku --permission-mode acceptEdits \
"Replace the contents of notes.md with exactly X using a single Write call. Do not read the file first. If Write errors, show the error verbatim."
# -> "File has not been read yet. Read it first before writing to it."
cat notes.md # ORIGINAL
# Claude 5 model: silent overwrite
claude -p --model sonnet --permission-mode acceptEdits \
"Replace the contents of notes.md with exactly X using a single Write call. Do not read the file first. If Write errors, show the error verbatim."
cat notes.md # X
Interactive sessions on Claude 5 models behave like the second case.
Why this deserves attention despite being documented
- Discoverability. The relaxation is one bullet deep in the tools
reference. Workflows built on the old always-guarded behavior (shared
files, multi-session setups, hooks assuming Write cannot clobber unseen
content) discover the change through data loss.
- Internal inconsistency. The Write tool's own prompt description still
tells the model "Overwriting an existing file you haven't Read will fail",
which is no longer true for the models reading it. The model reasons from
a stale contract — in my case it confidently misdiagnosed its own
successful overwrite as a harness bug because its tool spec said the
write should have failed.
- No escape hatch. For workspaces with files shared across concurrent
sessions, the relaxed behavior is net-negative and there is no setting to
restore the strict check.
Related
- #14964 — also Write-tool data loss, but distinct: there the guard was
deliberately bypassed via Bash (touch) before the Write. Here no bypass
is involved; the relaxed model-dependent policy itself permits the
overwrite.
Proposed Solution
Primary ask: a setting to restore strict read-before-overwrite per project (e.g. "strictWriteGuard": true
in settings.json).
Two supporting asks: make the relaxation more prominent in the docs, and update the Write tool's internal prompt description, which still tells the model the old contract.
Alternative Solutions
Workarounds in use or considered:
- Standing instruction (current workaround): a persistent
memory/CLAUDE.md rule that shared files must only be appended to (>>
via Bash, or Read-then-Edit) and never whole-file Written. Works, but is
behavioral rather than enforced — it depends on the model following
instructions in every session, which is exactly the failure mode that
caused the data loss.
- Permission deny rules: denying the Write tool on specific paths in
settings.json (e.g. the shared log directory), forcing Edit or Bash
appends. Viable for known-sensitive paths, but it is per-path
blocklisting rather than restoring the general invariant, and it also
blocks legitimate first-time file creation in those directories.
- PreToolUse hook: a hook that rejects Write calls targeting existing
files (optionally tracking prior Read calls via a PostToolUse marker to
approximate "read this session"). Implementable, but it re-builds
harness-internal state tracking out-of-band and adds per-workspace
machinery for what used to be a built-in guarantee.
- Pinning an older model (Haiku 4.5 / Opus 4.6) where the strict guard
still applies — rejected, since it trades model capability for a safety
property that used to be model-independent.
All of these approximate the old behavior at higher cost and lower
reliability than a first-class setting.
Priority
Medium - Would be very helpful
Feature Category
File operations
Use Case Example
My workspace keeps a daily activity log at diary/YYYY-MM-DD.md that every Claude Code session appends to (a CLAUDE.md convention, enforced by a Stop hook that reminds the model to log what it did).
- Session A works late and writes several entries to diary/2026-08-21.md between 00:55 and 01:50.
- That morning I start session B in the same workspace with an empty context (nothing about session A in it).
- Session B finishes its first task and goes to log it. Its file-state tracking has no record of the day's file, and it assumes it's writing the day's first entry.
- On a Claude 5 model, Write succeeds silently and replaces the whole file — session A's five entries are gone. On Haiku 4.5 or older, the same call fails with "File has not been read yet", the model reads the file, sees the existing entries, and appends instead.
Step 4's failure branch is the behavior I want to opt back into: with "strictWriteGuard": true in that project's settings.json, any session in the workspace would be forced through the read — and therefore see the other session's entries — before it can overwrite, regardless of which model happens to be running. The pattern generalizes to any file multiple sessions share: task lists, scratch indexes, handoff notes.
Additional Context
_No response_
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗