Yeachan-Heo/oh-my-codex · error · Error
worktree_prune_failed
worktree_prune_failed
Error message
worktree_prune_failed:${worktreePath} What it means
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.
Source
Thrown at src/team/worktree.ts:200
path: resolve(worktreeLine.slice('worktree '.length)),
head: headLine.slice('HEAD '.length).trim(),
branchRef: branchLine ? branchLine.slice('branch '.length).trim() : null,
detached: lines.includes('detached') || !branchLine,
});
}
return entries;
}
function pruneStaleWorktreePath(repoRoot: string, worktreePath: string): void {
const result = spawnSync('git', ['worktree', 'prune'], {
cwd: repoRoot,
encoding: 'utf-8',
windowsHide: true,
});
if (result.status === 0) return;
const stderr = (result.stderr || '').trim();
throw new Error(stderr || `worktree_prune_failed:${worktreePath}`);
}
function resolveBranchName(input: WorktreePlanInput): string | null {
if (!input.mode.enabled || input.mode.detached) return null;
if (input.scope === 'launch') {
return input.mode.name;
}
if (input.scope === 'autoresearch') {
const runTag = sanitizePathToken(input.worktreeTag || 'run');
return `autoresearch/${sanitizePathToken(input.mode.name)}/${runTag}`;
}
const workerName = (input.workerName || '').trim();
if (!workerName) {
throw new Error('team_worktree_worker_name_required');
}View on GitHub (pinned to 3ad79a8a6f)
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
Example fix
# before: prune fails on stale entry # after: git worktree unlock .omx/worktrees/<name> 2>/dev/null || true git worktree prune rm -rf .git/worktrees/<name> # last resort if prune still fails rm -rf .omx/worktrees/<name>
Defensive patterns
Strategy: try-catch
Validate before calling
function canPrune(repoRoot: string): boolean {
return spawnSync('git', ['worktree','list'], { cwd: repoRoot, encoding: 'utf-8' }).status === 0;
} Try / catch
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; } Prevention
- Clean up worktrees with git commands only, never raw filesystem deletes
- Unlock worktrees before removing them
- Serialize concurrent team starts to avoid prune races
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- worktree_rollback_failed
- (result.stderr || '').trim() || `git ${args.join(' ')} faile
- (result.stderr || '').trim() || `git status failed for ${wor
- autoresearch_reset_requires_clean_worktree
- stale_team_artifacts:${teamName}:${worktreePaths.length}_wor
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/18be253bbade3ab3.
Report an issue: GitHub.