can1357/oh-my-pi · error · GitCommandError
GitCommandError: git command failed (nonzero exit)
Error message
GitCommandError: git command failed (nonzero exit)
What it means
Generic failure path of _run(): when the wrapped git subprocess exits with any nonzero code (other than the timeout sentinel 124), a GitCommandError is raised carrying the command, exit code, stdout and stderr. It is the sandbox's way of surfacing 'git said no' with full output so callers (and the event's last_error) can diagnose the specific git failure.
Source
Thrown at python/robomp/src/sandbox.py:392
cmd: list[str],
*,
cwd: Path | None = None,
timeout: float | None = _DEFAULT_SANDBOX_SUBPROCESS_TIMEOUT,
) -> subprocess.CompletedProcess[str]:
"""Legacy raising helper (still used by a sandbox test). Forwards to subprocess.run."""
try:
proc = subprocess.run(
cmd,
cwd=str(cwd) if cwd else None,
check=False,
capture_output=True,
text=True,
timeout=timeout,
)
except subprocess.TimeoutExpired as exc:
raise GitCommandError(cmd, 124, "", f"git timed out after {timeout:.0f}s") from exc
if proc.returncode != 0:
raise GitCommandError(cmd, proc.returncode, proc.stdout, proc.stderr)
return proc
def _worktree_add(add_cmd: list[str], *, pool: Path, repo_dir: Path) -> None:
"""Run `git worktree add`, cleaning partial state on failure.
A worktree-add killed mid-operation (the 120s `_run` timeout surfaces as
GitCommandError 124, or any nonzero git failure) can leave a partial
checkout at `repo_dir` and/or a dangling pool worktree registration. Left
behind, the event retry hits stale metadata and fails again on the same
path. Best-effort remove the checkout and prune the pool, then re-raise so the
retry starts from a clean path. If the prune itself fails (incl. a 124
timeout), raise that instead — chained from the add error — since a
dangling registration left behind is exactly what poisons the retry.
"""
try:
_run(add_cmd, cwd=pool)
except GitCommandError as add_err:View on GitHub (pinned to 9690622007)
Solutions
- Read exc.stderr — it names the actual git problem (e.g. 'already exists', 'already checked out', 'not a repository').
- For 'already exists': remove the stale worktree dir (`git worktree remove --force` or rm + `git worktree prune`) and retry.
- For 'already checked out': detach or remove the conflicting worktree, or pass a start point that allows a new checkout.
- If the pool clone itself is gone, delete the workspace root and re-run ensure_workspace to trigger a fresh clone.
Example fix
// before
ws = manager.ensure_workspace(...) # fails: repo_dir exists from crashed run
// after
try:
ws = manager.ensure_workspace(...)
except GitCommandError as e:
if b"already exists" in (e.stderr or b""):
shutil.rmtree(ws_root / "repo", ignore_errors=True)
subprocess.run(["git", "worktree", "prune"], cwd=pool)
ws = manager.ensure_workspace(...) Defensive patterns
Strategy: try-catch
Validate before calling
if not (pool / ".git").exists():
manager.ensure_clone(repo=repo, clone_url=url, default_branch=main) # rebuild pool first Try / catch
try:
ws = manager.ensure_workspace(...)
except GitCommandError as e:
stderr = e.stderr or b""
if b"already exists" in stderr:
shutil.rmtree(repo_dir, ignore_errors=True); subprocess.run(["git","worktree","prune"],cwd=pool); retry()
elif b"already checked out" in stderr:
detach_or_remove_conflicting_worktree(); retry()
else:
raise Prevention
- Always branch cleanup logic on e.stderr contents, not the generic exit code.
- After any crash, prune worktree state before retrying ensure_workspace.
- Never hand-delete worktree dirs without `git worktree prune` in the pool.
When it happens
Trigger: Any git invocation through _run failing — `git worktree add` when repo_dir already exists or the branch is checked out elsewhere, `git branch -m` collisions, ref updates refused, or 'not a git repository' if the pool clone is missing/corrupt.
Common situations: Retrying ensure_workspace for an issue whose repo_dir survived a prior crash (worktree add sees an existing directory); trying to check out a branch already checked out in another worktree; a half-deleted pool after disk cleanup.
Related errors
- GitCommandError: git checkout --detach failed (nonzero exit)
- Isolated subagent execution could not be prepared: ${message
- could not find an unused worktree path under ${basePath} (tr
- git timed out after {effective_timeout:.0f}s: {' '.join(_red
- GitCommandError(cmd, proc.returncode, proc.stdout, proc.stde
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/1f11ff0621f7a711.
Report an issue: GitHub.