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

  1. Remove the stale metadata manually: `git worktree prune` from the repo root; if that fails, delete .git/worktrees/<name> directory and the stale path
  2. Unlock locked worktrees: `git worktree unlock <path>` then prune
  3. Check filesystem permissions on .git and the stale worktree path; fix ownership if git reports unsafe repository
  4. 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

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


AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27). Data as JSON: /api/errors/18be253bbade3ab3. Report an issue: GitHub.