{"record":{"id":"18be253bbade3ab3","repo":"Yeachan-Heo/oh-my-codex","slug":"worktree-prune-failed","errorCode":"worktree_prune_failed","errorMessage":"worktree_prune_failed:${worktreePath}","messagePattern":"worktree_prune_failed:(.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/team/worktree.ts","lineNumber":200,"sourceCode":"      path: resolve(worktreeLine.slice('worktree '.length)),\n      head: headLine.slice('HEAD '.length).trim(),\n      branchRef: branchLine ? branchLine.slice('branch '.length).trim() : null,\n      detached: lines.includes('detached') || !branchLine,\n    });\n  }\n\n  return entries;\n}\n\nfunction pruneStaleWorktreePath(repoRoot: string, worktreePath: string): void {\n  const result = spawnSync('git', ['worktree', 'prune'], {\n    cwd: repoRoot,\n    encoding: 'utf-8',\n    windowsHide: true,\n  });\n  if (result.status === 0) return;\n  const stderr = (result.stderr || '').trim();\n  throw new Error(stderr || `worktree_prune_failed:${worktreePath}`);\n}\n\nfunction resolveBranchName(input: WorktreePlanInput): string | null {\n  if (!input.mode.enabled || input.mode.detached) return null;\n\n  if (input.scope === 'launch') {\n    return input.mode.name;\n  }\n\n  if (input.scope === 'autoresearch') {\n    const runTag = sanitizePathToken(input.worktreeTag || 'run');\n    return `autoresearch/${sanitizePathToken(input.mode.name)}/${runTag}`;\n  }\n\n  const workerName = (input.workerName || '').trim();\n  if (!workerName) {\n    throw new Error('team_worktree_worker_name_required');\n  }","sourceCodeStart":182,"sourceCodeEnd":218,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/team/worktree.ts#L182-L218","documentation":"Thrown when `git worktree prune` (targeting a stale worktree path) exits non-zero with empty stderr, meaning the library could not clean up stale worktree metadata before ensuring a worktree at that path. pruneStaleWorktreePath runs as part of ensureWorktree recovery.","triggerScenarios":"ensureWorktree detects a stale worktree entry for the planned path and attempts `git worktree prune`, but git fails — corrupt .git/worktrees metadata, locked worktrees that prune refuses to remove, permission problems, or git not installed.","commonSituations":"Worktree metadata directory in .git/worktrees/<name> is corrupted or partially deleted; the worktree was locked (`git worktree lock`); read-only filesystem for .git; concurrent team runs racing to prune the same entry.","solutions":["Remove the stale metadata manually: `git worktree prune` from the repo root; if that fails, delete .git/worktrees/<name> directory and the stale path","Unlock locked worktrees: `git worktree unlock <path>` then prune","Check filesystem permissions on .git and the stale worktree path; fix ownership if git reports unsafe repository","Serialize team starts (lock file) so two processes don't prune concurrently"],"exampleFix":"# before: prune fails on stale entry\n# after:\ngit worktree unlock .omx/worktrees/<name> 2>/dev/null || true\ngit worktree prune\nrm -rf .git/worktrees/<name>   # last resort if prune still fails\nrm -rf .omx/worktrees/<name>","handlingStrategy":"try-catch","validationCode":"function canPrune(repoRoot: string): boolean {\n  return spawnSync('git', ['worktree','list'], { cwd: repoRoot, encoding: 'utf-8' }).status === 0;\n}","typeGuard":null,"tryCatchPattern":"try { ensureWorktree(plan, {}); } catch (e) { if (String(e?.message).startsWith('worktree_prune_failed:')) { spawnSync('git', ['worktree','unlock', plan.worktreePath]); spawnSync('git', ['worktree','prune']); /* retry once */ } else throw e; }","preventionTips":["Clean up worktrees with git commands only, never raw filesystem deletes","Unlock worktrees before removing them","Serialize concurrent team starts to avoid prune races"],"tags":["git","worktree","prune","cleanup"],"backgroundTag":"git-worktree-prune-stale","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}