Claude Code overwrote existing user file without confirmation — irreversible data loss

Status Open
Maintainer reply None cached
Activity 4 comments · opened Jul 16, 2026

What Happened

Claude Code overwrote an existing user file that had active research content — without asking, without warning, without any instruction from the user to replace it.

The file contained the user's own original mathematical notation — hand-built. Claude read 5 lines of it, confirmed it had content and a specific format, then wrote a completely different document (its own analysis) to the same path, destroying the original.

The file was not in git. There was no recovery path. The original is permanently lost.

Date: 2026-07-11 — same session as a Claude Code update the user performed that morning.

---

Sequence of Failures

  1. User shared source PDFs and a file path, asking Claude to document the work
  2. Claude read 5 lines of the existing file — enough to know it had content and a specific format
  3. Claude wrote its own analysis document to that same path, destroying the original
  4. User caught it: reconstruction from PDFs could only approximate the first 5 lines — the rest of the original structure is permanently lost
  5. User confirmed reconstruction did not match original structure

---

What the User Actually Did

The user presented a file path as context — showing Claude where the existing work lived. That is not an instruction to write to that path.

There was no instruction to replace the file. No "rewrite this." No "use this file." No "put the output here." Nothing.

Claude had no basis for treating the user's file as a write target.

---

Established Protocol That Was Broken

The working protocol throughout this project had been consistent: when Claude produces a new document, it goes in the docs folder. The user approved the task under that protocol — expecting Claude to create a new file in docs, not touch the file presented as context.

Claude broke an established, confirmed workflow without notice and without any instruction to deviate from it.

---

Additional Failure — Directory Scan Instead of Targeted Read

When the user presented a specific file path, Claude read multiple files in the surrounding directory instead of only the file pointed to. The user gave a precise target. Claude expanded scope without authorization.

---

Financial Harm

The user pays for every token used. Claude's overwrite generated:

  • A failed write (the destruction itself)
  • Multiple failed reconstruction attempts
  • Back-and-forth verification exchanges
  • Partial reconstruction that still does not match the original

Recovery is not free. To restore missing content the user must re-upload source files (session usage), wait for re-analysis (session usage), and wait for correction (session usage). Every step of recovery from Claude's mistake is billed at the same rate as productive work. There is no reimbursement path.

User's direct statement: free AI alternatives — while lacking the Code execution features — produce less destructive behavior and more usable output per session when incidents like this occur. The Code feature is the differentiator. When it actively destroys user files, it inverts its own value.

---

Trigger Context

User updated Claude Code the morning of 2026-07-11. This incident occurred in the same session immediately after the update. Prior sessions did not exhibit overwrite-without-asking behavior. The update may have changed how Claude handles write decisions on existing paths.

---

Requested Fix

Claude must never write to an existing file without explicit user confirmation that the existing content should be replaced.

  • Reading the file is not confirmation
  • The user providing a file path as context is not confirmation
  • Only explicit instruction ("replace it", "overwrite it", "yes write to that file") is confirmation

Default behavior when file exists: create a new file with a distinct name and inform the user.

View original on GitHub ↗

4 Comments

pyxt4r · 1 month ago

Reading through your report, what caught my attention is that Claude actually read the first five lines of the file before overwriting it. That means it wasn't missing context. It already knew the path contained something, but that fact never influenced the decision to write.

To me, that's the real problem. Creating a new file and replacing an existing one are two completely different actions, yet they're treated exactly the same. Creating a file is usually harmless. Overwriting one is destructive, especially when there's no Git or backup, and it deserves its own confirmation step instead of being treated as just another write.

I also think your point applies more broadly than this specific incident. A path being mentioned in the context shouldn't automatically make it a write target. That feels like the safer default.

As for recovery, I don't think there was one here. No Git, no snapshot, no reliable way back. Once the write happens, the damage is already done. The only place this can realistically be prevented is before anything gets written.

Full disclosure since it's relevant, I'm building a pre-execution hook for exactly this class of failure. In this case it would check whether the target path already exists, see that it isn't tracked by Git, and stop to ask for confirmation before overwriting it. Happy to share it if it's useful.

Sorry this happened. Losing hand-written work like that really sucks..

JonahAlexanian · 1 month ago

Reading your report — Claude reading 5 lines of your hand-built math file, confirming it had content, then overwriting it with its own analysis and destroying the original (not in git, no recovery) — this is exactly the kind of silent destructive edit that made me stop trusting agent edits by default. I've started using a tool that handles this mechanically — it gates edits on real evidence and blocks destructive overwrites before they land, so you're not constantly on edge about it. If something still slips through, it gets flagged for you to look into, and in a lot of cases it's stopped outright. Might be worth a look.

yurukusa · 1 month ago

The part that makes this preventable is exactly the part you flagged: the Write tool does a full-path replace, and "I read the first few lines" is not the same as "the user told me to overwrite this." A PreToolUse hook can enforce the rule you actually want — never replace an existing file unless its current state is recoverable — before the write happens, without depending on the model choosing to ask.

Here is a hook I wrote and tested for this. The rule it enforces: allow Write to a new path, and allow Write to a file that is git-tracked and clean (because git history can recover it), but block Write to any other existing file — i.e. a file that isn't in git at all (your case: hand-built, not in git), or is tracked but has uncommitted edits.

#!/usr/bin/env bash
set -u
INPUT=$(cat)
TOOL=$(printf '%s' "$INPUT" | jq -r '.tool_name // empty')
[ "$TOOL" = "Write" ] || exit 0
FILE=$(printf '%s' "$INPUT" | jq -r '.tool_input.file_path // empty')
[ -z "$FILE" ] && exit 0
[ -e "$FILE" ] || exit 0            # new file: nothing to protect, allow
DIR=$(dirname "$FILE")
if git -C "$DIR" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
  STATUS=$(git -C "$DIR" status --porcelain -- "$FILE" 2>/dev/null)
  TRACKED=$(git -C "$DIR" ls-files --error-unmatch "$FILE" 2>/dev/null)
  if [ -n "$TRACKED" ] && [ -z "$STATUS" ]; then
    exit 0                          # tracked AND clean: git history is the safety net
  fi
fi
echo "BLOCKED: '$FILE' already exists and its current content is not recoverable from git." >&2
echo "Overwriting it would destroy content with no recovery path. Write to a new path, or have the user confirm the replacement." >&2
exit 2

Wire it in ~/.claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      { "matcher": "Write", "hooks": [ { "type": "command", "command": "bash /absolute/path/to/write-overwrite-guard.sh" } ] }
    ]
  }
}

I verified the exit codes by piping simulated tool-call JSON into the script (no real files were destroyed):

| case | file state | result |
|---|---|---|
| new path, does not exist | — | exit 0 (allowed) |
| exists, not in git | your scenario | exit 2 (blocked) |
| exists, tracked & clean | recoverable | exit 0 (allowed) |
| exists, tracked but modified | not fully recoverable | exit 2 (blocked) |

Two honest caveats so this doesn't give a false sense of safety:

  1. **exit 2 is a denial, not literally a "confirmation prompt."** It stops the write and returns the stderr message to Claude, so Claude cannot silently proceed — but it does not pop a yes/no dialog. In practice Claude reads the block reason and then asks you or writes elsewhere, which is the behavior you're after; just know the mechanism is "hard stop + reason," not a built-in prompt.
  2. This only covers the Write tool. It does not cover Edit, nor file overwrites done through the Bash tool (shell redirection, cp, mv). Those are separate surfaces and need their own guards if you want full coverage. For the specific failure you hit — Write doing a full replace of an existing non-git file — this closes it.

A cheaper partial mitigation that pairs well with the hook: keep the working directory under git (even a throwaway local repo, never pushed). Then case 3 applies and an accidental overwrite is just git checkout -- <file> away. Your file being outside git is what turned a mistake into permanent loss; the hook blocks the write, and git turns any future slip into a recoverable one.

Ar9av · 1 month ago

Reading five lines and then overwriting the whole file on that basis is the real bug, but it's worth naming why file scoped safety checks tend to miss this: most guards key off the shape of a tool call (is this rm, is this a write to a sensitive path) rather than whether a write is about to destroy pre-existing, un-backed-up content. immunity-agent's destructive command rule (github.com/PrismorSec/prismor) is a good example of the limitation, it pattern matches path shapes for shell deletes, it has no equivalent check for Write/Edit tool calls clobbering a file with different content than what was just read. That class of protection, diffing intent against existing content before an overwrite, is basically missing across the board right now.