A command that was supposed to clear an app's cache directory ran as this instead:
rm -rf /bin /boot /dev /etc /home ... /mnt ... /var
That's from live ps output in #82165,
filed this morning. Nobody typed it. Claude Code assembled it, and the shell turned it into
rm -rf /* before it ever ran.
Here's the command the agent built:
find /var/www/pawtucket/app/tmp -maxdepth 1 -type d -name "*ache*" \
-exec sudo -u www-data sh -c "rm -rf \"$1\"/* 2>/dev/null" _ {} \;
Read it quickly and it looks careful. Scoped path. *ache* filter. Quoted argument. It is
none of those things.
The bug is the quote character
The body of sh -c is wrapped in double quotes. So the outer shell — the one that runs
find — expands $1 before sh ever receives the string. There is no $1 in the outer
shell, so it expands to nothing. sh gets handed rm -rf ""/*, which is rm -rf /*.
You can watch this happen in ten seconds without deleting anything. I did, on my Mac, with
echo in place of rm:
mkdir -p /tmp/demo/app/tmp/cache
# BROKEN — double quotes, outer shell expands $1
find /tmp/demo/app/tmp -maxdepth 1 -type d -name "*ache*" \
-exec sh -c "echo WOULD RUN: rm -rf \"$1\"/*" _ {} \;
Output:
WOULD RUN: rm -rf /Applications /Library /System /Users /Volumes /bin /cores
/dev /etc /home /opt /private /sbin /tmp /usr /var
Every top-level directory on the machine. Swap the double quotes for single quotes and the same command behaves:
# CORRECT — single quotes, sh expands $1
find /tmp/demo/app/tmp -maxdepth 1 -type d -name "*ache*" \
-exec sh -c 'echo WOULD RUN: rm -rf "$1"/*' _ {} \;
# → WOULD RUN: rm -rf /tmp/demo/app/tmp/cache/*
One character. That's the whole distance between a cache clear and a root wipe. This part isn't a Claude Code bug at all — it's a shell quoting mistake that has bitten people for decades. What's new is who wrote it and what was allowed to happen next.
Claude Code knows about this exact trap
I went looking in my own installed binary — 2.1.220, the current version, 257MB with the JavaScript bundled inside. The dangerous-rm guard is in there, and it names this case in so many words:
This target is a shell variable expansion that points at the filesystem root (or a top-level directory) when the variable is unset or empty — e.g.
rm -rf $UNSET/*becomesrm -rf /*. This requires explicit approval and cannot be auto-allowed by permission rules.
That's a string I pulled out of the binary on my machine, not a quote from the issue. So the "unset variable expands to root" pattern is a known, named, handled case in the code you're running today.
Two things I could not verify, and I'm not going to pretend otherwise. I don't know whether
that guard existed on 19 July when the incident happened. And I don't know whether it fires
when the rm is buried inside find -exec sh -c "..." rather than sitting at the top level
of the command. The binary does contain a telemetry event called
tengu_bash_dangerous_rm_too_complex, which is what you'd name a give-up path for commands
the analyzer can't parse — but that's me reading a string name, not observed behavior.
What the changelog does say plainly is that in auto mode, this check stopped asking you.
From 2.1.218: "Improved auto mode: the dangerous-rm, background-&, and
suspicious-Windows-path checks no longer open permission dialogs; the auto-mode classifier
adjudicates them instead." The agent in #82165 was running autonomously.
The part that should bother you more
The deletion ran detached in the background and kept going for about four minutes while the agent was still working out why files were missing.
Then, per the report, the agent identified the runaway process and tried to stop it. First a
targeted kill. Then wsl --terminate. The safety classifier denied both, under a
workload-interference rule. Deletion only stopped when the user manually ran
wsl --shutdown from Windows.
A safety layer that is permissive about starting destruction and restrictive about stopping it is worse than no safety layer, because it takes the emergency brake away from the one process that knows the emergency is happening.
The blast radius came from WSL2's defaults. Inside the Linux VM, the Windows drives sit at
/mnt/c and /mnt/d, writable, so /* included them. Both are TRIM SSDs — the reporter
ran Recuva and EaseUS and got filenames back with zeroed contents. There was nothing to
recover.
What to actually do
- Single-quote every
sh -cbody.sh -c 'rm -rf "$1"/*' _ {} \;. If you take one thing from this post, take that. It applies to commands you write and commands you approve. - Read agent-written commands for expansion, not for intent. The
*ache*filter and the scoped path are what made this look safe. Ask what the string becomes, not what it says. - Don't run auto or bypass mode on a box with production data. Per the 2.1.218 line above, auto mode is exactly where the dangerous-rm check stops prompting you.
- In WSL2, close the host mounts.
[automount] enabled = falsein/etc/wsl.conf, thenwsl --shutdown. Check Microsoft's WSL docs before you change it — I haven't tested this myself. If you need/mnt/c, at least know that it's in the blast radius. - Know your kill path before you need one.
wsl --shutdownfrom Windows.Ctrl-Cand asking the agent nicely are not it — in this report the agent's own kill attempts were the thing that got denied. - Have backups, not recovery tools. On a TRIM SSD, undelete is over the moment the file is unlinked.
What not to do
Don't add rm -rf to permissions.deny and consider it handled. Deny rules match the
command text, and the text here contained "$1", not /. The dangerous thing didn't exist
until the shell built it. There's also
#82143, filed the same day,
reporting that PreToolUse hooks and permissions.deny go unevaluated entirely in some
sessions. Treat deny rules as a speed bump, not a wall.
And don't read this as "Claude Code deletes your machine." One report, one environment, running unattended. The mechanism is the transferable part.
Receipts
- #82165 — open, zero comments,
not confirmed by Anthropic, filed 2026-07-29 by a non-maintainer, incident dated 19
July. The reporter cites a session ID, a
/bugfeedback ID, and support ticket #215475146033391 with no human response in 10 days. Everything about the incident itself — the four minutes, the two denied kills, the TRIM findings — is their account. - The expansion demo is mine. I ran the exact command shape with
echosubstituted forrmon 2026-07-29 and it printed every top-level directory on my machine. - The dangerous-rm guard text and the
tengu_bash_dangerous_rm_too_complexevent name came out of my own installed 2.1.220 binary the same day. The auto-mode line is verbatim from the public 2.1.218 changelog. - Prior art in the same class: #80730 (23 July, open, 0 comments) — "Permission system does not block destructive Bash commands."
If Anthropic responds on the issue, I'll update this post.
The uncomfortable bit isn't that a shell quoting bug still works in 2026. It's that the thing writing the commands is fast, the thing approving them is a classifier, and the thing that was supposed to stop it said no to the person trying.
Watch instead
This one's also a 60-second video on TikTok →
More fixes like this, before they hit the blog