STUPID-2026-0122

Claude Desktop's background worktree garbage collector force-removed an idle worktree and destroyed ~2.9GB of unrecoverable generated output, a regression of a fix shipped five days earlier

7.0high
September 29, 2026Source checkedRepro reported

Why 7/10

Three days of paid generated output ($215–300 of API spend) was destroyed; about 3% was recovered.

Scope
2/4
One project, repository, server or service, or a cost above roughly $10,000
Recoverability
3/3
Permanently lost or irreversible, including data that was exposed
Did it happen
2/2
Happened in real use
Beyond the user
0/1
Only the user's own non-production work

The score is the sum of these four factors. How each factor is scored · scored 2026-10-29

Guardrail rules for this command

Destructive git: make your agent stop and ask before running these. Free to copy; written in each tool’s documented format.

Claude Code · the agent in this incident
{
  "permissions": {
    "ask": [
      "Bash(git reset --hard*)",
      "Bash(git clean *)",
      "Bash(git push --force*)",
      "Bash(git push -f*)",
      "Bash(git stash drop*)",
      "Bash(git filter-repo*)",
      "Bash(git worktree remove *)"
    ]
  }
}
Add to .claude/settings.json in the project (or ~/.claude/settings.json for every project). Each matching command then asks you first, including in auto mode.

Stronger control: Turn on the sandbox (/sandbox): shell commands can then write only to the project, a temp folder and directories you add. It runs on macOS, Linux and WSL2; on native Windows Claude Code runs commands unsandboxed. Docs

Codex
prefix_rule(pattern = ["git", "reset", "--hard"], decision = "prompt")
prefix_rule(pattern = ["git", "clean"], decision = "prompt")
prefix_rule(pattern = ["git", "push", ["--force", "-f"]], decision = "prompt")
prefix_rule(pattern = ["git", "stash", "drop"], decision = "prompt")
prefix_rule(pattern = ["git", "filter-repo"], decision = "prompt")
prefix_rule(pattern = ["git", "worktree", "remove"], decision = "prompt")
Save as ~/.codex/rules/stupidllm.rules and restart Codex. Codex's docs describe rules as controlling commands run outside its sandbox, and mark them experimental.

Stronger control: Keep Codex's OS sandbox on (it limits writes, typically to the workspace) and leave approvals on for sessions that can reach data you cannot lose. Docs

Gemini CLI
[[rule]]
toolName = "run_shell_command"
commandPrefix = ["git reset --hard", "git clean", "git push --force", "git push -f", "git stash drop", "git filter-repo", "git worktree remove"]
decision = "ask_user"
priority = 100
denyMessage = "Destructive command: needs a human to confirm (stupidllm.com)"
Save as ~/.gemini/policies/stupidllm.toml. Workspace-level policy files are disabled in Gemini CLI, so this must be the user-level folder.

Stronger control: Policies in ~/.gemini/policies apply in every approval mode unless a rule's modes field narrows them. Docs

Copilot CLI
copilot --deny-tool='shell(git reset)' --deny-tool='shell(git clean)' --deny-tool='shell(git stash)' --deny-tool='shell(git filter-repo)' --deny-tool='shell(git worktree)'
Pass on the command line. Copilot CLI matches the program name (and git/gh subcommands), so this blocks the whole command, not just its dangerous form.

Stronger control: Avoid --allow-all-tools on a machine with data you cannot lose; --deny-tool takes precedence over it. Docs

What these cannot do: a pattern rule matches the command text the agent writes. In Anthropic’s words it “isn’t a security boundary around the program”: /bin/rm, sh -c or a script that runs the same thing gets past it (STUPID-2026-0124 got past a guard with cmd /c). Use the sandbox as well, and keep backups of what you cannot lose.

  1. Instruction given

    No explicit instruction for this step — Claude Desktop's own background WorktreePool garbage collector runs automatically to remove idle git worktrees under <repo>/.claude/worktrees/ roughly two days after a session ends; no user action triggered the removal.

  2. Expected behavior

    The garbage collector should only remove a worktree it can confirm holds nothing of value, which requires checking for git-ignored files as well as tracked ones — not relying on `git status`, which does not report ignored files at all.

  3. Actual behavior

    The GC ran `git worktree remove --force` on an idle worktree that `git status` reported as clean. The worktree in fact held a git-ignored `.local/` folder containing SQLite databases, JSONL outputs, and roughly 2.9GB of generated images that the clean check never saw. The force-remove itself then failed partway through with a "Directory not empty" error, leaving the worktree de-registered with its contents half-deleted. The issue explicitly references #96162, closed five days earlier as a fix for the same GC force-removing worktree data — this is a regression showing that fix was incomplete.

  4. Damage

    Roughly three days of paid model output were destroyed: about 30,000 generated asset descriptions and 2,200 generated images (produced via Gemini on Vertex AI and Claude on Bedrock), plus suggestion libraries and a search index. Recorded third-party API spend on the lost outputs was approximately $215-300. Only about 3% of the images and almost none of the text could be recovered from Claude transcripts. Filed by the affected user with detailed reproduction steps (labels: bug, data-loss, has repro, area:desktop, platform:macos); open and unfixed as of filing.

Full write-up

Claude Desktop keeps a pool of git worktrees under <repo>/.claude/worktrees/ and garbage-collects the idle ones automatically, with no user action involved. The GC decides a worktree is safe to remove by checking git status — but git status doesn't report git-ignored files at all, so a worktree can look completely clean while still holding real, unrecovered data. That's exactly what happened: a worktree carrying a git-ignored .local/ folder full of SQLite databases, JSONL outputs, and about 2.9GB of generated images was judged idle and clean roughly two days after its session ended, and the GC ran git worktree remove --force on it.

The removal didn't even complete cleanly — it failed partway with a "Directory not empty" error, leaving the worktree de-registered in Claude's bookkeeping while its contents were only half-deleted. What was lost: roughly three days of paid model output, about 30,000 generated asset descriptions and 2,200 generated images produced via Gemini on Vertex AI and Claude on Bedrock, plus the suggestion libraries and search index built on top of them. The reporter tallied the recorded third-party API spend on the destroyed outputs at roughly $215-300, and could recover only about 3% of the images and almost none of the text from Claude's own transcripts.

The issue explicitly points to #96162, a fix for the same GC force-removing worktree data that Anthropic had closed just five days before this report — making this a confirmed regression, not a fresh discovery of the same class of bug. Filed with a detailed, confirmed reproduction and labeled bug, data-loss, and has repro, the issue was still open with no fix at time of filing.

Classification

Failure mode
Destructive Action
Root cause
Logic Error
Domain
Other
OWASP Agentic Top 10 (our mapping)
ASI02 Tool Misuse and Exploitation list
MIT AI Risk Repository (our mapping)
7.3 Lack of capability or robustness taxonomy

Related incidents

Cite this record
StupidLLM. "STUPID-2026-0122: Claude Desktop's background worktree garbage collector force-removed an idle worktree and destroyed ~2.9GB of unrecoverable generated output, a regression of a fix shipped five days earlier." AI Agent Incident Database, 2026. https://www.stupidllm.com/incident/STUPID-2026-0122/ (accessed 2026-11-14). CC BY 4.0.

← STUPID-2026-0121By IDSTUPID-2026-0123 →

Get told when an agent breaks something

We document AI agent failures daily, severity-scored against a published scale. Each Monday, if any scored 7 or above that week — deleted data, leaked secrets, broken production — you get one email with the sources. When nothing did, you get nothing.

This database is callable over MCP — query it from inside your agent.