[BUG] In 2.1.141, stale-worktree cleanup uses `rm -rf` as a fallback and silently deletes untracked code

Resolved 💬 2 comments Opened May 14, 2026 by officiallygeorge Closed May 16, 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?

Working on an epic with multiple sub-issues.

  • 16:28:15 – I approve agent to merge PR 273 with gh pr merge 273 --squash --delete-branch before going back to resume work on issue 268 which we had started in the same session before realizing that it was blocked on 273.
  • 16:34:26 – The agent spins up a background process running a post-merge benchmark script that takes 10-15 minutes to complete and writes logs.
  • 16:37:34 – Claude Code's stale-worktree cleanup detects the squash-merged branch and attempts to run git worktree remove --force on .claude/worktrees/sweet-davinci-fce058
  • 16:37:34 – The command fails with error: failed to delete '...sweet-davinci-fce058': Directory not empty because benchmarks are still writing to the directory and the untracked changes for issue 268 are still present.
  • 16:37:34 – ~Library/Logs/Claude/main.log shows: [warn] Git worktree remove failed, will try manual cleanup. Manual cleanup is, completely insanely, rm -rf.
  • 16:45:52 – Agent wakes and realizes that worktree has been deleted: [info] [CCD] Worktree at ...sweet-davinci-fce058 was deleted; falling back to origin repo.

It looks like the cleanup runs ten minutes or so after any squash-merge irrespective of what's running in the worktree, and if it cannot remove the worktree via git worktree remove it uses rm -rf without warning the user.

What Should Happen?

Let me put this part in big letters:

AT NO POINT SHOULD CLAUDE CODE RUN rm -rf ON A WORKTREE WITH UNTRACKED CHANGES IN IT.

  1. Do not rely on the 'archive' status of a session in Claude Desktop – the new auto-archiving sensitivity is broken and infuriating. I spend significant time hitting the charmlessly-labeled "Unarchive" button in response to the app archiving sessions that it thinks are idle (but are not).
  2. When git worktree remove --force fails, instead of falling back to SILENTLY IRREVOCABLY DELETING EVERYTHING, use a cheap model to investigate and either move the untracked files + worktree to a temporary .claude/orphaned/-type path, or propose a plan to the user.

Error Messages/Logs

Steps to Reproduce

  1. In a Claude Code session, start work on a feature branch inside an auto-created agent worktree at <repo>/.claude/worktrees/<name>.
  2. Open and squash-merge a PR for that worktree's branch (e.g. gh pr merge <N> --squash --delete-branch).
  3. Before ~9 minutes elapse, do one of (each independently reproduces):
  • Leave modified-but-uncommitted files in the worktree, OR
  • Start a long-running process (e.g. docker compose run ... with a volume mount writing into the worktree's output/ directory).
  1. Wait until Claude Code's stale-worktree cleanup polls the PR state (observed: ~9 min after the merge).
  2. Cleanup runs git -c core.longpaths=true worktree remove --force <worktree> and gets error: failed to delete '<worktree>': Directory not empty. Then a [warn] Git worktree remove failed, will try manual cleanup message is logged.
  3. A few seconds later, the worktree directory is gone. Any in-flight process that was writing into it crashes with FileNotFoundError / ENOENT. Any uncommitted edits the user made are lost.

Claude Model

Opus

Is this a regression?

Yes, this worked in a previous version

Last Working Version

_No response_

Claude Code Version

2.1.140

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Other

Additional Information

_No response_

View original on GitHub ↗

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