{"record":{"id":"1f11ff0621f7a711","repo":"can1357/oh-my-pi","slug":"gitcommanderror-git-command-failed-nonzero-exit","errorCode":null,"errorMessage":"GitCommandError: git command failed (nonzero exit)","messagePattern":"GitCommandError: git command failed \\(nonzero exit\\)","errorType":"exception","errorClass":"GitCommandError","httpStatus":null,"severity":"error","filePath":"python/robomp/src/sandbox.py","lineNumber":392,"sourceCode":"    cmd: list[str],\n    *,\n    cwd: Path | None = None,\n    timeout: float | None = _DEFAULT_SANDBOX_SUBPROCESS_TIMEOUT,\n) -> subprocess.CompletedProcess[str]:\n    \"\"\"Legacy raising helper (still used by a sandbox test). Forwards to subprocess.run.\"\"\"\n    try:\n        proc = subprocess.run(\n            cmd,\n            cwd=str(cwd) if cwd else None,\n            check=False,\n            capture_output=True,\n            text=True,\n            timeout=timeout,\n        )\n    except subprocess.TimeoutExpired as exc:\n        raise GitCommandError(cmd, 124, \"\", f\"git timed out after {timeout:.0f}s\") from exc\n    if proc.returncode != 0:\n        raise GitCommandError(cmd, proc.returncode, proc.stdout, proc.stderr)\n    return proc\n\n\ndef _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:","sourceCodeStart":374,"sourceCodeEnd":410,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/python/robomp/src/sandbox.py#L374-L410","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nws = manager.ensure_workspace(...)  # fails: repo_dir exists from crashed run\n// after\ntry:\n    ws = manager.ensure_workspace(...)\nexcept GitCommandError as e:\n    if b\"already exists\" in (e.stderr or b\"\"):\n        shutil.rmtree(ws_root / \"repo\", ignore_errors=True)\n        subprocess.run([\"git\", \"worktree\", \"prune\"], cwd=pool)\n        ws = manager.ensure_workspace(...)","handlingStrategy":"try-catch","validationCode":"if not (pool / \".git\").exists():\n    manager.ensure_clone(repo=repo, clone_url=url, default_branch=main)  # rebuild pool first","typeGuard":null,"tryCatchPattern":"try:\n    ws = manager.ensure_workspace(...)\nexcept GitCommandError as e:\n    stderr = e.stderr or b\"\"\n    if b\"already exists\" in stderr:\n        shutil.rmtree(repo_dir, ignore_errors=True); subprocess.run([\"git\",\"worktree\",\"prune\"],cwd=pool); retry()\n    elif b\"already checked out\" in stderr:\n        detach_or_remove_conflicting_worktree(); retry()\n    else:\n        raise","preventionTips":["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."],"tags":["git","subprocess","worktree"],"backgroundTag":"git-command-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}