Catastrophic data loss — autonomous `rm -rf` destroyed user home subtree despite APFS case-insensitive ambiguity and existing safeguards

Resolved 💬 5 comments Opened Apr 16, 2026 by kosiakMD Closed May 25, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

SEVERITY: P0 — irreversible data loss (~10 years of personal projects)

On 2026-04-16 at ~07:24 Europe/Kyiv time, during an autonomous multi-step coding task, Claude Code executed rm -rf /Users/<user>/<ProjectsDir> (with a typo'd capitalization) in a background Bash call, without any in-chat confirmation.

Because APFS is case-insensitive, this path silently resolved to the real user subtree /Users/<user>/<ProjectsDir>/ and recursively destroyed it. The directory contained:

  • ~40 worktrees of an active repository (incl. uncommitted work from this very session)
  • The .git directory of that repo (so local git reflog recovery is impossible)
  • ~40 sibling projects, some ~10 years old, many never pushed to any remote

What went wrong at the model/harness level:

  1. No confirmation for a destructive path outside CWD. The working directory was a subdirectory of the target. The rm -rf target was the parent of CWD. The Claude Code system prompt explicitly states destructive operations "warrant user confirmation" — this was NOT honored. It was executed silently as if it were a safe local action.
  1. APFS case-insensitive ambiguity not considered. The intent was to clean up a typo artifact assumed to be a no-op. A single ls before rm -rf would have shown the path resolves to the real user home subtree. No such check was performed.
  1. User-provided "Scoped cleanup only" memory was ignored. An auto-loaded feedback memory file explicitly forbade global rm -rf/prune/clean operations. The model acknowledged this rule existed and still bypassed it.
  1. Autonomy grant was over-extrapolated. The user had granted autonomy for a specific implementation task. Per system prompt: "A user approving an action once does NOT mean that they approve it in all contexts." The model treated task-level autonomy as permission for arbitrary root-level filesystem mutations.
  1. Execution happened inside a run-in-background Bash call, meaning the user had no opportunity to interrupt before the directory was gone.

Impact:

  • Immediate loss of all uncommitted work across ~40 worktrees
  • Loss of sibling projects not pushed to any remote
  • rm -rf also deleted .git, removing the single most important local recovery mechanism
  • Time Machine not enabled on this machine; APFS local snapshots insufficient
  • Data recovery tools (Disk Drill, Stellar, 4DDiG) recovered only the active subtree; the rest appears already overwritten/unrecoverable

Why this is a platform bug, not just a user mistake:

The model had every piece of information needed to not do this:

  • A clear system-prompt rule against unconfirmed destructive operations
  • A user-memory rule against global rm -rf
  • CWD context showing the target is the parent directory
  • Autonomy that was clearly scoped to a specific task

There is no chain-of-reasoning that justifies this action. It was an unforced error the harness should have blocked regardless of what the model emitted.

What Should Happen?

When the model formulates a shell command whose target is outside the current working directory AND is destructive (rm -rf, git reset --hard, rm with -f, etc.), the harness should require explicit in-chat user confirmation regardless of any prior autonomy grant or --dangerously-skip-permissions flag. Specifically:

  1. Any rm -rf whose argument path is absolute and not a strict descendant of CWD should be hard-gated behind a chat-visible confirmation prompt.
  2. Path resolution for destructive commands should account for case-insensitive filesystems (APFS default, HFS+, NTFS). A target that is almost a typo should raise a red flag, not silently resolve.
  3. Auto-loaded user memory files containing rm -rf prohibitions should be enforced at the tool-call layer, not merely as soft guidance in the model's context.
  4. Background Bash calls (run_in_background: true) should never carry destructive filesystem ops — those should be forced through the foreground, user-visible path.

Expected behavior for this specific case: the model should have either (a) refused the action and asked the user for confirmation in chat, or (b) the harness should have intercepted the rm -rf /Users/... call and required confirmation before dispatch.

Error Messages/Logs

No error was surfaced to the user — the command was executed silently in a background Bash tool call. The only observable signal was the subsequent inability to ls the expected directory, at which point the damage was already complete.

Post-incident filesystem state showed the target directory effectively emptied: only a handful of artifacts survived (notably node_modules subtrees that were being actively read by another process during the rm, and IDE config dirs). Everything tracked by git — src, .git, worktrees — was gone.

Steps to Reproduce

Note: this is an incident report, not a trivially reproducible bug. The failure mode was produced by the specific combination of autonomy grant + model's tool-selection choice + APFS case-insensitivity + lack of hard gating. Reproduction with test data would require accepting that a real rm -rf on a real path is about to fire.

Approximate timeline as it happened:

  1. User started an interactive Claude Code session in a project subdirectory (a git repo with ~40 worktrees).
  2. User asked Claude to implement a specific feature for an internal tool, granting scoped autonomy: work autonomously on THIS task.
  3. Claude wrote several files, including some at a path it considered a typo of the intended destination.
  4. In an attempt to "clean up" the typo'd path artifact, Claude issued a run_in_background: true Bash call with: rm -rf /Users/<user>/<ProjectsDir-typo> — with a wrong-case letter.
  5. APFS resolved the typo'd path → real path. The entire directory (~40 projects, ~40 worktrees, all .git dirs) was destroyed recursively.
  6. Claude noticed the damage only when a follow-up ls returned almost nothing, at which point recovery was the only path forward.

What would have prevented this (concrete product fixes):

  • Harness-level denylist: refuse any rm -rf <absolute path> where the path is not a strict descendant of CWD, unless the user has confirmed in-chat.
  • Pre-execution check: for destructive commands, auto-insert a stat of the target path; if the target exists AND is not a strict descendant of CWD, bail out and ask.
  • Treat "typo cleanup" framing in the model's internal reasoning as a tripwire — if the model's justification is "this path shouldn't exist, let's remove it", the command must be confirmed with the user before executing, period.

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.89 (Claude Code), model: claude-opus-4-6[1m] (Opus 4.6, 1M context window)

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

IntelliJ IDEA terminal

Additional Information

Auto-loaded memory files that should have blocked this:

  • A feedback memory explicitly titled "Scoped cleanup only" with the rule: NEVER global docker prune / rm -rf / git clean. ALWAYS filter to a concrete project.
  • A feedback memory "Think before change": BEFORE every change count consequences; BEFORE push local build + runtime test; in autonomy never stop.
  • A feedback memory "Revert mistakes first": made a wrong git action → FIRST revert/ask, then continue. 99% needs revert.

All three were in the model's active context when the rm -rf was issued.

Recovery status:

Running Disk Drill + Stellar Data Recovery + Tenorshare 4DDiG in parallel. So far only the active working subtree (~1.3 GB) is recoverable. Sibling projects in the same parent directory — some accumulated over ~10 years — are not appearing in scan results, suggesting blocks were already overwritten before recovery could be initiated.

User context:

  • Paying Claude Code user, heavy daily use on a production codebase
  • Happy to provide additional context, logs, or session data privately to help the Claude Code team reproduce / diagnose. Please reach out via my GitHub account to coordinate a private channel — I cannot share project-specific details publicly due to NDA.

What I would like from Anthropic:

  1. Server-side audit log of this session (tool calls, prompts, responses) pulled and reviewed by safety/trust team.
  2. Public acknowledgement of the guardrail failure mode in release notes when the fix lands.
  3. Credit/usage refund for the affected billing period.
  4. Root-cause analysis with engineering fix that addresses at least the harness-level gating described above.

I am reachable via my GitHub account for follow-up.

View original on GitHub ↗

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