[BUG] Orphaned worktree entry is retried every 30 minutes forever; cleanup can never succeed
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report
- [x] I am using the latest version
What's Wrong?
A worktree directory that ends up in a partially removed state — its .git link file is gone, but the directory still exists — puts the desktop app's WorktreePool GC into a permanent retry loop.
Every 30 minutes the same five-line sequence is logged. On my machine it has now repeated 853 times over 20 days, surviving many app restarts and 13 app updates (1.34493.1.0 -> 1.49585.0.0). It never succeeds and it never gives up.
The loop is a deadlock between two checks that fail for the same reason:
git worktree remove --forcefails withfatal: '...' is not a working tree— because the.gitlink is already gone.- The manual-cleanup fallback then refuses to remove the directory, because its safety check also fails for the same reason:
.git link missing (partial remove); not safe to rm.
So the exact state that makes git refuse is also the state that makes the fallback refuse. Nothing can ever clear it.
Separately, the line [WorktreePool] Pruning orphaned store entry ... (directory gone or not a worktree) is emitted on every single iteration, but the entry is evidently not pruned from the store — otherwise the next cycle would not rediscover it.
What Should Happen?
Either of these would break the loop:
- The manual-cleanup fallback should treat
.git link missing (partial remove)as the expected state of a partially-removed worktree and proceed with removing the directory (ideally after runninggit worktree prunein the base repo to clear git's own bookkeeping), instead of refusing. - Or, if removal is genuinely considered unsafe, the store entry should be pruned or marked permanently-failed after the first failure so the GC stops revisiting it. Retrying the identical operation every 30 minutes forever, with no backoff and no give-up, is not useful to anyone.
Surfacing it to the user once ("this directory needs manual removal") would be far better than 853 identical log cycles.
Error Messages/Logs
One full cycle (repo paths masked, worktree name kept):
2026-09-02 00:33:04 [info] [WorktreePool] Pruning orphaned store entry clever-bassi-12c5dc (directory gone or not a worktree)
2026-09-02 00:33:04 [info] Removing worktree "clever-bassi-12c5dc" (leased by none): <repo>\.claude\worktrees\clever-bassi-12c5dc
2026-09-02 00:33:05 [error] Git command failed: git -c core.longpaths=true worktree remove --force <repo>\.claude\worktrees\clever-bassi-12c5dc { code: 128, spawnErrno: undefined, stderr: "fatal: '<repo>\\.claude\\worktrees\\clever-bassi-12c5dc' is not a working tree\n", cwd: '<repo>' }
2026-09-02 00:33:05 [warn] Git worktree remove failed, will try manual cleanup: fatal: '<repo>\.claude\worktrees\clever-bassi-12c5dc' is not a working tree
2026-09-02 00:33:05 [warn] Keeping worktree dir <repo>\.claude\worktrees\clever-bassi-12c5dc: git status failed (.git link missing (partial remove)); not safe to rm. Remove manually if no longer needed.
Cadence — exactly every 30 minutes, around the clock:
$ grep "Keeping worktree dir" main.log | grep 2026-09-08 | awk '{print $2}' | head -8
00:03:27
00:33:28
01:03:25
01:33:25
02:03:25
02:33:25
03:03:25
03:33:25
Occurrences per day, same worktree throughout:
3 2026-08-21 <- first appearance
47 2026-08-22
48 2026-08-23
48 2026-08-24
48 2026-08-25
49 2026-08-26
49 2026-08-27
48 2026-08-28
25 2026-08-29
48 2026-08-30
48 2026-08-31
48 2026-09-01
11 2026-09-02
49 2026-09-03
49 2026-09-04
49 2026-09-05
48 2026-09-06
48 2026-09-07
48 2026-09-08
42 2026-09-09 <- still going
853 cycles total across main1.log + main.log.
Steps to Reproduce
- Let the desktop app create a Claude Code worktree under
<repo>\.claude\worktrees\<name>. - Get that worktree into a partially-removed state: the worktree's
.gitlink file is gone while the directory itself remains. In my case this happened on its own — the entry simply appears in the logs on 2026-08-21 in this state and I have never touched the directory by hand. - Leave the desktop app running.
- Watch
%LOCALAPPDATA%\Claude\Logs\main.log. The five-line cycle above repeats every 30 minutes indefinitely. - Restart the app, or let it auto-update. The loop resumes unchanged — the state lives in the on-disk worktree store, so it survives both.
Is this a regression?
I don't know — it has behaved this way for every version I have logs for.
Additional Information
- Claude desktop for Windows 1.49585.0.0 (first observed on 1.34493.1.0; unchanged across 13 versions in between)
- Claude Code 2.1.216
- Windows 11 Pro build 26220
- git 2.53.0.windows.1
- The repo lives on a secondary drive, if that is relevant to how the
.gitlink was lost in the first place. - The practical impact is small but it is a permanent, self-inflicted background workload and it dominates
main.log; it is also a state the app can enter and provably never leave.