[BUG] Orphaned worktree entry is retried every 30 minutes forever; cleanup can never succeed

Status Fixed / completed
Reported on v2.1.216
Maintainer reply None cached
Activity 0 comments · opened Sep 9, 2026 · closed Sep 10, 2026

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:

  1. git worktree remove --force fails with fatal: '...' is not a working tree — because the .git link is already gone.
  2. 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 running git worktree prune in 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

  1. Let the desktop app create a Claude Code worktree under <repo>\.claude\worktrees\<name>.
  2. Get that worktree into a partially-removed state: the worktree's .git link 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.
  3. Leave the desktop app running.
  4. Watch %LOCALAPPDATA%\Claude\Logs\main.log. The five-line cycle above repeats every 30 minutes indefinitely.
  5. 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 .git link 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.

View original on GitHub ↗