{"record":{"id":"8e4c447a6d0b902e","repo":"stablyai/orca","slug":"git-still-reports-a-registration-for-worktreepa","errorCode":null,"errorMessage":"Git still reports a registration for \"${worktreePath}\" after pruning it.","messagePattern":"Git still reports a registration for \"(.+?)\" after pruning it\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/main/git/worktree.ts","lineNumber":1254,"sourceCode":"    await gitExecFileAsync(\n      ['worktree', 'remove', '--force', worktreePath],\n      gitExecOptions(repoPath, registrationOptions)\n    )\n    return\n  } catch (error) {\n    console.warn(\n      `[git] Failed to deregister the moved worktree \"${worktreePath}\"; pruning instead`,\n      error\n    )\n  }\n\n  await gitExecFileAsync(['worktree', 'prune'], gitExecOptions(repoPath, registrationOptions))\n  // Strict (not the shared scan): an unreadable repo must not read as proof that the row is gone.\n  const stillRegistered = (await listWorktreesStrict(repoPath, registrationOptions)).some(\n    (worktree) => areWorktreePathsEqual(worktree.path, worktreePath)\n  )\n  if (stillRegistered) {\n    throw new Error(`Git still reports a registration for \"${worktreePath}\" after pruning it.`)\n  }\n}\n\nasync function deleteBranchAfterWorktreeRemoval(\n  repoPath: string,\n  branchName: string,\n  branchHead: string,\n  options: RemoveWorktreeOptions\n): Promise<RemoveWorktreeResult> {\n  try {\n    // Why: also drop the now-orphaned branch so delete-worktree leaves none; `-d` (not `-D`) preserves\n    // unmerged work, and forceBranchDelete opts into `-D` for failed-creation rollback.\n    const branchDeleteResult = await deleteLocalBranchAfterWorktreeRemoval(\n      repoPath,\n      branchName,\n      options.forceBranchDelete === true,\n      options\n    )","sourceCodeStart":1236,"sourceCodeEnd":1272,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/git/worktree.ts#L1236-L1272","documentation":"Thrown by clearGitRegistrationForMissingWorktree after both `git worktree remove --force` AND `git worktree prune` ran, yet listWorktreesStrict (the strict, non-shared variant that does not silently treat an unreadable repo as empty) still returns a row for that path. It is a fail-closed guard: the removal left git's administrative state inconsistent, so the code refuses to report success. The strict scan is deliberate — an unreadable repo must not be misread as proof the row is gone.","triggerScenarios":"Removing a worktree whose directory was already deleted out-of-band while git's `.git/worktrees/<name>/` admin entry still references a stale absolute path; on Windows when a file handle (editor, indexer, antivirus) keeps the directory locked past prune; a worktree admin directory that lost its `gitdir` pointer so prune skips it; filesystem permissions that let prune read but not unlink the admin lock.","commonSituations":"Antivirus or search indexer holding a handle on Windows; the worktree was moved manually rather than via `git worktree move`; NFS/SMB mount flakiness where prune reads stale metadata; a concurrently running `git gc` rewriting worktree metadata mid-prune.","solutions":["Run `git worktree list --porcelain` in the main repo and confirm the stale row is still present.","Manually `git worktree remove --force <path>` again, then `git worktree prune --verbose` and check the admin dir `.git/worktrees/<name>/` is gone.","On Windows, close editors, stop the search indexer on that path, and retry removal.","If the admin dir lingers, remove `.git/worktrees/<basename>/` by hand once you have confirmed no live worktree points at it.","Retry the Orca removal once the manual prune succeeds."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Pre-check that the worktree admin entry exists and is prunable.\nimport { existsSync } from 'node:fs'\nimport { join } from 'node:path'\nfunction worktreeAdminEntryExists(repoPath: string, worktreeBasename: string): boolean {\n  return existsSync(join(repoPath, '.git', 'worktrees', worktreeBasename))\n}","typeGuard":null,"tryCatchPattern":"try {\n  await removeWorktree(repoPath, worktreePath, options)\n} catch (err) {\n  if (/still reports a registration/.test((err as Error).message)) {\n    // prompt the user to manually prune or close handles on Windows\n    promptManualPrune(repoPath, worktreePath)\n  } else throw err\n}","preventionTips":["Always remove worktrees via `git worktree remove`, never `rm -rf`.","On Windows, close editors and exclude worktree dirs from the search indexer.","Run `git worktree prune` periodically to clear stale admin entries."],"tags":["git","worktree","prune","fail-closed","windows"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}