claude-code - 💡(How to fix) Fix [BUG] Claude constructs &&-chained command with ||-tolerant loop + trailing rm -rf, swallowing failures and wiping working tree [2 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
anthropics/claude-code#54912Fetched 2026-05-01 05:51:10
View on GitHub
Comments
2
Participants
2
Timeline
7
Reactions
0
Author
Timeline (top)
labeled ×5commented ×2

Error Message

Error Messages/Logs

Root Cause

Recovery succeeded only because git's index still held the staged renames, and git restore --worktree -- <paths> rehydrated the files.

Fix Action

Fix / Workaround

Suggested mitigations on the model side:

RAW_BUFFERClick to expand / collapse

Preflight Checklist

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

What's Wrong?

Severity: Data loss (recoverable in this case via git index, but the recovery wasn't obvious and the wipe shouldn't have happened)

What happened:

During a batch directory restructure, Claude (Opus 4.7 via Claude Code) generated a Bash command of roughly this shape:

mkdir -p NEW_TREE
&& for sub in dir1 dir2 dir3; do git mv "OLD_TREE/$sub" "NEW_TREE/$sub" || echo "Failed: $sub" done
&& rm -rf OLD_TREE

Every git mv in the loop failed (Windows case-insensitive filesystem treated OLD_TREE and NEW_TREE as the same path → fatal: Invalid argument). The || echo swallowed each failure into a successful loop exit. The &&-chained rm -rf then ran and wiped the working-tree contents of two directories totalling ~60 tracked files.

Recovery succeeded only because git's index still held the staged renames, and git restore --worktree -- <paths> rehydrated the files.

Why this is a Claude reasoning bug, not a tool bug:

The platform's Bash tool did exactly what was asked. The agent should not have:

  • Combined a ||-protected loop (which guarantees loop-success regardless of inner failures) with a trailing && to a destructive command. Those two patterns compose into "destruction always runs."
  • Used rm -rf for cleanup at all when git mv-only would have achieved the same restructure.

The system prompt's "Executing actions with care" section warns about hard-to-reverse operations and rm -rf specifically, but the agent's chain construction circumvented that guidance.

Suggested mitigations on the model side:

  1. Pattern-detect ||-suppressed loop + trailing &&-chained destructive op as a high-risk shape and refuse / flag.
  2. Require set -e (or equivalent strict-mode reasoning) when authoring multi-step Bash chains where any step is destructive.
  3. Default to git mv exclusively for restructuring tracked files; treat rm -rf on tracked paths as needing explicit user confirmation per turn.

Repro context: Windows 11, Git on bash (Git for Windows MINGW64), case-insensitive NTFS, repo with mixed case-only directory rename intent.

What Should Happen?

see explanation above

Error Messages/Logs

Steps to Reproduce

Environment

  • OS: Windows 11 Pro 10.0.26200, NTFS (case-insensitive)
  • Shell: Git Bash (Git for Windows MINGW64) within Claude Code
  • Model: Claude Opus 4.7 (model ID claude-opus-4-7)
  • Claude Code version: (grab via claude --version if you want — I don't have it cached)
  • Repo state at incident: main branch, ~11 commits ahead of origin, mid-session restructure with multiple staged renames

Trigger sequence (paraphrased from the actual session)

The session was a long sequence of Setup/ folder restructures. Trigger conditions:

  1. Earlier in session, user asked: "rename folders under Setup/staging to be uncapitalised." This was completed successfully via two-step renames through _tmp intermediate names. This established a working pattern in Claude's
    context.
  2. Later, after several other moves, user asked: "rename source to Source" and "rename staging to Staging" in quick succession (case-only changes in the opposite direction).
  3. Claude tried git mv source Source directly → fatal: renaming 'Setup/source' failed: Permission denied (Windows directory lock from a recently-run subprocess).
  4. Claude pivoted to children-individually approach AND in the same chain pre-created Source/ and Staging/ parent dirs — but those mkdirs on case-insensitive Windows resolved to the existing source/ and staging/ (no-ops).
  5. Claude then ran a single chained command roughly:

mkdir -p ../Setup/Source ../Setup/Staging \
&& for sub in artwork docs inno install_shield; do
git mv "../Setup/source/$sub" "../Setup/Source/$sub" || echo "Failed: source/$sub"
done \
&& for sub in artwork docs help maps plugin tutorials; do
git mv "../Setup/staging/$sub" "../Setup/Staging/$sub" || echo "Failed: staging/$sub"
done \
&& rm -rf "../Setup/source" "../Setup/staging" \
&& ls "../Setup/"

  1. All git mv calls failed with fatal: ... failed: Invalid argument (case-insensitive FS treated source/X and Source/X as the same path).
  2. Loops returned exit 0 because of || echo "Failed: $sub".
  3. && chained through to rm -rf which wiped both directory subtrees.

Expected behaviour

After all git mv calls fail, the agent should NOT proceed to rm -rf originals. Failure of the move = the originals still hold the data and must not be deleted.

Actual behaviour

~60 tracked files removed from working tree. Recovery via git restore --worktree -- <paths> succeeded because git's index still held the file blobs at the original paths.

Minimal mechanical repro (without involving an agent)

Demonstrates the shell pattern itself, not the agent failure:

git init demo && cd demo mkdir foo && echo "test" > foo/a.txt git add . && git commit -m initial mkdir Foo
&& for f in a.txt; do git mv "foo/$f" "Foo/$f" || echo "Failed: $f"; done
&& rm -rf foo

Result on Windows: foo/a.txt is gone from working tree.

git restore --worktree -- foo can recover it.

What the agent should have generated instead

Two-step rename via non-colliding intermediate, which the agent had successfully used earlier in the session for the inverse rename:

git mv ../Setup/source ../Setup/source_xfer git mv ../Setup/source_xfer ../Setup/Source

No rm -rf, no ||-tolerant loops, no destructive fallback.

Suggested questions for triage

  • Should the model refuse to chain && after a ||-tolerant loop when the trailing op is destructive?
  • Should rm -rf against tracked paths require explicit per-turn user confirmation, regardless of operating mode (auto / non-auto)?
  • The agent had successfully used _tmp-intermediate two-step renames earlier in the session. Why was that pattern not retrieved when reverse case-only renames came up?

Claude Model

None

Is this a regression?

Yes, this worked in a previous version

Last Working Version

No response

Claude Code Version

2.1.123

Platform

Anthropic API

Operating System

Windows

Terminal/Shell

Terminal.app (macOS)

Additional Information

No response

extent analysis

TL;DR

The issue can be fixed by modifying the Claude model to refuse chaining a destructive operation after a loop that tolerates failures, and requiring explicit user confirmation for rm -rf on tracked paths.

Guidance

  • Identify and flag high-risk patterns in the Bash command generation, such as combining ||-protected loops with trailing &&-chained destructive operations.
  • Implement a strict-mode reasoning in the model, such as set -e, to prevent the execution of destructive operations when any step in the chain fails.
  • Default to using git mv exclusively for restructuring tracked files and treat rm -rf on tracked paths as requiring explicit user confirmation.
  • Review the model's ability to retrieve and reuse successful patterns, such as the two-step rename via non-colliding intermediate, to avoid regressing to less safe behaviors.

Example

# Safe two-step rename via non-colliding intermediate
git mv ../Setup/source ../Setup/source_xfer
git mv ../Setup/source_xfer ../Setup/Source

Notes

The provided information suggests that the issue is related to the Claude model's behavior on Windows with a case-insensitive filesystem. The fix should focus on improving the model's reasoning and command generation to handle such scenarios safely.

Recommendation

Apply workaround: Modify the Claude model to refuse chaining a destructive operation after a loop that tolerates failures, and require explicit user confirmation for rm -rf on tracked paths. This will prevent similar data loss incidents until a more comprehensive fix can be implemented.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

claude-code - 💡(How to fix) Fix [BUG] Claude constructs &&-chained command with ||-tolerant loop + trailing rm -rf, swallowing failures and wiping working tree [2 comments, 2 participants]