{"record":{"id":"6cd870f1a49098b0","repo":"can1357/oh-my-pi","slug":"gitcommanderror-git-worktree-prune-failed-nonzer","errorCode":null,"errorMessage":"GitCommandError: git worktree prune failed (nonzero exit)","messagePattern":"GitCommandError: git worktree prune failed \\(nonzero exit\\)","errorType":"exception","errorClass":"GitCommandError","httpStatus":null,"severity":"error","filePath":"python/robomp/src/sandbox.py","lineNumber":414,"sourceCode":"def _worktree_add(add_cmd: list[str], *, pool: Path, repo_dir: Path) -> None:\n    \"\"\"Run `git worktree add`, cleaning partial state on failure.\n\n    A worktree-add killed mid-operation (the 120s `_run` timeout surfaces as\n    GitCommandError 124, or any nonzero git failure) can leave a partial\n    checkout at `repo_dir` and/or a dangling pool worktree registration. Left\n    behind, the event retry hits stale metadata and fails again on the same\n    path. Best-effort remove the checkout and prune the pool, then re-raise so the\n    retry starts from a clean path. If the prune itself fails (incl. a 124\n    timeout), raise that instead — chained from the add error — since a\n    dangling registration left behind is exactly what poisons the retry.\n    \"\"\"\n    try:\n        _run(add_cmd, cwd=pool)\n    except GitCommandError as add_err:\n        shutil.rmtree(repo_dir, ignore_errors=True)\n        pruned = _safe_run([\"git\", \"worktree\", \"prune\"], cwd=pool)\n        if pruned.returncode != 0:\n            raise GitCommandError(\n                [\"git\", \"worktree\", \"prune\"], pruned.returncode, pruned.stdout, pruned.stderr\n            ) from add_err\n        raise\n\n\n_SHARED_OMP_GID = 2000\n\n\ndef _slot_permissions_active(slot_uid: int | None) -> bool:\n    return slot_uid is not None and platform.system() == \"Linux\" and os.geteuid() == 0\n\n\ndef _slot_pids(slot_uid: int, proc_root: Path = Path(\"/proc\")) -> tuple[int, ...]:\n    \"\"\"Return non-zombie process ids owned by the slot UID.\n\n    Debian's slim image does not include procps/pkill. Reading `/proc` keeps\n    slot cleanup self-contained and avoids adding a runtime package only for\n    this one operation.","sourceCodeStart":396,"sourceCodeEnd":432,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/python/robomp/src/sandbox.py#L396-L432","documentation":"When `git worktree add` fails, _worktree_add deletes the partially created repo_dir and attempts `git worktree prune` to clean the pool's stale worktree registrations. If the prune itself fails, its GitCommandError is chained from the original add error and raised — signaling the pool's worktree metadata is in a bad state that automatic cleanup could not fix.","triggerScenarios":"ensure_workspace / ensure_release_workspace hitting a worktree add failure (existing dir, taken branch) where the follow-up `git worktree prune` also exits nonzero — typically because the pool clone's .git/worktrees metadata is corrupt, the pool dir was concurrently mutated, or permissions/locks block the prune.","commonSituations":"A previous git process crashed mid-worktree-registration leaving malformed .git/worktrees entries; the shared pool clone was modified by hand or by another tool; running with a slot_uid whose filesystem permissions cannot write the pool metadata.","solutions":["Fix pool metadata: run `git worktree prune -v` manually in the pool clone and inspect `.git/worktrees/` for corrupt entries; delete bogus subdirectories.","Check permissions/ownership of the pool directory (slot_uid GID sharing) — prune must be able to write the pool's .git.","Remove the stale repo_dir and the whole workspace root, then retry so ensure_clone/ensure_workspace rebuild state.","Escalate to recreating the pool clone (delete pool dir; ensure_clone re-clones) if metadata stays corrupt."],"exampleFix":"# before\n# event stuck failing with chained prune error\n# after\n# operator recovery inside the pool\nsubprocess.run([\"git\", \"worktree\", \"prune\", \"-v\"], cwd=pool)\nshutil.rmtree(ws_root, ignore_errors=True)\nws = manager.ensure_workspace(...)  # rebuilds clean","handlingStrategy":"try-catch","validationCode":"chk = subprocess.run([\"git\",\"worktree\",\"prune\",\"--dry-run\"], cwd=pool, capture_output=True)\nmetadata_ok = chk.returncode == 0","typeGuard":null,"tryCatchPattern":"try:\n    ws = manager.ensure_workspace(...)\nexcept GitCommandError as e:\n    if \"worktree prune\" in str(getattr(e, 'cmd', '')):\n        alert_operator_pool_corrupt(pool)  # needs manual .git/worktrees repair\n    else:\n        schedule_retry(event)","preventionTips":["Never mutate the pool clone's .git by hand or with external tools.","Run git as the same user/GID (slot_uid) that robomp uses, so prune can write pool metadata.","After hard kills, run `git worktree prune -v` in the pool proactively."],"tags":["git","worktree","cleanup"],"backgroundTag":"git-worktree-prune-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}