can1357/oh-my-pi · error · GitCommandError

GitCommandError(cmd, proc.returncode, proc.stdout, proc.stde

Error message

GitCommandError(cmd, proc.returncode, proc.stdout, proc.stderr)

What it means

`_check` inspects a completed `git` subprocess and raises `GitCommandError(cmd, returncode, stdout, stderr)` whenever the exit code is non-zero. This is the library's single generic git-failure path: the exception carries the exact command, git's exit code, and both (credential-redacted) output streams, so the real cause is in `proc.stderr` on the exception.

Source

Thrown at python/robomp/src/git_ops.py:287

        )
    except subprocess.TimeoutExpired as exc:
        # `subprocess.run` already kills the direct child when the timeout
        # fires, but we explicitly re-raise as `GitCommandError` so callers
        # don't have to special-case `TimeoutExpired` alongside the regular
        # non-zero-exit error path. 124 mirrors GNU `timeout`.
        stdout = redact_credentials(exc.stdout or "") if isinstance(exc.stdout, str) else ""
        stderr_msg = f"git timed out after {effective_timeout:.0f}s: {' '.join(_redacted_cmd(cmd))}"
        raise GitCommandError(cmd, 124, stdout, stderr_msg) from exc
    if proc.stdout:
        proc.stdout = redact_credentials(proc.stdout)
    if proc.stderr:
        proc.stderr = redact_credentials(proc.stderr)
    return proc


def _check(proc: subprocess.CompletedProcess[str], cmd: list[str]) -> subprocess.CompletedProcess[str]:
    if proc.returncode != 0:
        raise GitCommandError(cmd, proc.returncode, proc.stdout, proc.stderr)
    return proc


def _git_dir(repo_dir: Path) -> Path | None:
    dot_git = repo_dir / ".git"
    if dot_git.is_dir():
        return dot_git
    if dot_git.is_file():
        try:
            text = dot_git.read_text(encoding="utf-8").strip()
        except OSError:
            return None
        prefix = "gitdir:"
        if not text.startswith(prefix):
            return None
        git_dir = Path(text[len(prefix) :].strip())
        return git_dir if git_dir.is_absolute() else (repo_dir / git_dir).resolve()
    if (repo_dir / "HEAD").exists() and (repo_dir / "objects").is_dir():

View on GitHub (pinned to 9690622007)

Solutions

  1. Read `exc.stderr` from the GitCommandError — it contains git's own diagnostic (auth failure, rejected, not found)
  2. Verify credentials/token scope for the remote operation
  3. For push rejections, `git pull --rebase` / fetch and rebase locally before pushing again
  4. Check network/remote availability and the exact command recorded on the exception

Example fix

// before
push(repo_dir)  # GitCommandError, code unclear
// after
try:
    push(repo_dir)
except GitCommandError as exc:
    print(exc.cmd, exc.returncode, exc.stderr)
Defensive patterns

Strategy: try-catch

Validate before calling

from pathlib import Path
if not (Path(repo_dir) / ".git").exists():
    raise ValueError(f"{repo_dir} is not a git repository")

Try / catch

try:
    _check(proc, cmd)
except GitCommandError as exc:
    if exc.returncode == 128 and "Authentication" in (exc.stderr or ""):
        refresh_credentials(); retry()
    elif exc.returncode == 1 and "rejected" in (exc.stderr or ""):
        pull_rebase_then_retry()
    else:
        raise

Prevention

When it happens

Trigger: Any git invocation checked by `_check` failing: clone of a nonexistent/unauthorized repo, fetch_prune against a dead remote, push rejected (non-fast-forward, protected branch, permissions), fetch_pr_head for a missing PR ref.

Common situations: Expired/insufficient GitHub token, network DNS failure, remote branch deleted, merge conflict-ish non-fast-forward push, repository URL typo.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/d961c2a663772c78. Report an issue: GitHub.