{"record":{"id":"d961c2a663772c78","repo":"can1357/oh-my-pi","slug":"gitcommanderror-cmd-proc-returncode-proc-stdout","errorCode":null,"errorMessage":"GitCommandError(cmd, proc.returncode, proc.stdout, proc.stderr)","messagePattern":"GitCommandError\\(cmd, proc\\.returncode, proc\\.stdout, proc\\.stderr\\)","errorType":"exception","errorClass":"GitCommandError","httpStatus":null,"severity":"error","filePath":"python/robomp/src/git_ops.py","lineNumber":287,"sourceCode":"        )\n    except subprocess.TimeoutExpired as exc:\n        # `subprocess.run` already kills the direct child when the timeout\n        # fires, but we explicitly re-raise as `GitCommandError` so callers\n        # don't have to special-case `TimeoutExpired` alongside the regular\n        # non-zero-exit error path. 124 mirrors GNU `timeout`.\n        stdout = redact_credentials(exc.stdout or \"\") if isinstance(exc.stdout, str) else \"\"\n        stderr_msg = f\"git timed out after {effective_timeout:.0f}s: {' '.join(_redacted_cmd(cmd))}\"\n        raise GitCommandError(cmd, 124, stdout, stderr_msg) from exc\n    if proc.stdout:\n        proc.stdout = redact_credentials(proc.stdout)\n    if proc.stderr:\n        proc.stderr = redact_credentials(proc.stderr)\n    return proc\n\n\ndef _check(proc: subprocess.CompletedProcess[str], cmd: list[str]) -> subprocess.CompletedProcess[str]:\n    if proc.returncode != 0:\n        raise GitCommandError(cmd, proc.returncode, proc.stdout, proc.stderr)\n    return proc\n\n\ndef _git_dir(repo_dir: Path) -> Path | None:\n    dot_git = repo_dir / \".git\"\n    if dot_git.is_dir():\n        return dot_git\n    if dot_git.is_file():\n        try:\n            text = dot_git.read_text(encoding=\"utf-8\").strip()\n        except OSError:\n            return None\n        prefix = \"gitdir:\"\n        if not text.startswith(prefix):\n            return None\n        git_dir = Path(text[len(prefix) :].strip())\n        return git_dir if git_dir.is_absolute() else (repo_dir / git_dir).resolve()\n    if (repo_dir / \"HEAD\").exists() and (repo_dir / \"objects\").is_dir():","sourceCodeStart":269,"sourceCodeEnd":305,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/python/robomp/src/git_ops.py#L269-L305","documentation":"`_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.","triggerScenarios":"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.","commonSituations":"Expired/insufficient GitHub token, network DNS failure, remote branch deleted, merge conflict-ish non-fast-forward push, repository URL typo.","solutions":["Read `exc.stderr` from the GitCommandError — it contains git's own diagnostic (auth failure, rejected, not found)","Verify credentials/token scope for the remote operation","For push rejections, `git pull --rebase` / fetch and rebase locally before pushing again","Check network/remote availability and the exact command recorded on the exception"],"exampleFix":"// before\npush(repo_dir)  # GitCommandError, code unclear\n// after\ntry:\n    push(repo_dir)\nexcept GitCommandError as exc:\n    print(exc.cmd, exc.returncode, exc.stderr)","handlingStrategy":"try-catch","validationCode":"from pathlib import Path\nif not (Path(repo_dir) / \".git\").exists():\n    raise ValueError(f\"{repo_dir} is not a git repository\")","typeGuard":null,"tryCatchPattern":"try:\n    _check(proc, cmd)\nexcept GitCommandError as exc:\n    if exc.returncode == 128 and \"Authentication\" in (exc.stderr or \"\"):\n        refresh_credentials(); retry()\n    elif exc.returncode == 1 and \"rejected\" in (exc.stderr or \"\"):\n        pull_rebase_then_retry()\n    else:\n        raise","preventionTips":["Always inspect exc.stderr — git's message names the root cause","Keep tokens/credentials fresh and scoped to the remote operation","Fetch and rebase before pushing to avoid non-fast-forward rejections","Validate repo URLs and remote names before invoking git"],"tags":["git","subprocess","command-failed"],"backgroundTag":"git-command-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}