odysseus-dev/odysseus · error · RuntimeError
{cmd[0]} exited with {result.returncode}
Error message
{cmd[0]} exited with {result.returncode} What it means
Raised by _run_gh_json in scripts/pr_blocker_audit.py when a gh CLI subprocess (gh pr list or gh api) exits non-zero and produced no usable stderr text. The fallback message names the executable and its exit code. Because stderr is preferred, seeing this exact message means gh failed silently — typically auth, connectivity, or being killed.
Source
Thrown at scripts/pr_blocker_audit.py:255
if isinstance(payload, dict):
if warnings:
payload["warnings"] = [*payload.get("warnings", []), *warnings]
return payload
if warnings:
return {"items": payload, "warnings": warnings}
return payload
def _fetch_live_pr_files(repo: str, number: int) -> list[str]:
api_path = f"repos/{repo}/pulls/{number}/files?per_page=100"
payload = _run_gh_json(["gh", "api", "--paginate", api_path])
return _extract_files(payload)
def _run_gh_json(cmd: list[str]):
result = subprocess.run(cmd, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False)
if result.returncode != 0:
raise RuntimeError(result.stderr.strip() or f"{cmd[0]} exited with {result.returncode}")
try:
return json.loads(result.stdout or "[]")
except json.JSONDecodeError as exc:
raise RuntimeError(f"gh returned invalid JSON: {exc}") from exc
def normalize_prs(payload) -> list[PullRequest]:
raw_prs = payload.get("items", []) if isinstance(payload, dict) else payload
if raw_prs is None:
raw_prs = []
if not isinstance(raw_prs, list):
raise ValueError("expected input JSON to be a list of pull requests or an object with an items list")
return [normalize_pr(item) for item in raw_prs if isinstance(item, dict)]
def missing_file_metadata_count(prs: list[PullRequest]) -> int:
return sum(1 for pr in prs if not pr.files)
View on GitHub (pinned to f9235ebbf1)
Solutions
- Authenticate gh: gh auth login (or export GH_TOKEN=<pat>), then verify with gh auth status.
- Reproduce manually with the exact command from the traceback, e.g. gh pr list --repo <repo> --state open --limit 1000 --json ..., to see the real failure.
- Check network/proxy reachability of api.github.com from the environment running the script.
- Confirm the gh binary exists and is a real gh (command -v gh; gh --version).
- If the failure is transient (rate limit, 5xx), wait and re-run the audit — fetch is retried fresh each invocation.
Example fix
# before $ python scripts/pr_blocker_audit.py --repo org/repo --live RuntimeError: gh exited with 1 # after $ gh auth status # find expired/missing credential $ gh auth login # or: export GH_TOKEN=ghp_xxx $ python scripts/pr_blocker_audit.py --repo org/repo --live
Defensive patterns
Strategy: retry
Validate before calling
import subprocess
def gh_ready() -> bool:
try:
r = subprocess.run(['gh', 'auth', 'status'], capture_output=True, text=True, timeout=15)
return r.returncode == 0
except (OSError, subprocess.TimeoutExpired):
return False Type guard
import shutil, subprocess
def gh_usable() -> bool:
if not shutil.which('gh'):
return False
return gh_ready() Try / catch
for attempt in range(3):
try:
prs = fetch_live_prs(repo)
break
except RuntimeError as e:
msg = str(e)
if 'exited with' in msg and attempt < 2:
time.sleep(2 ** attempt) # transient gh failure (auth expiry is NOT transient: fix gh auth)
continue
raise Prevention
- Run gh auth status as a preflight in any script that shells out to gh.
- Export GH_TOKEN with a token whose expiry outlives the job, and rotate on a schedule.
- Wrap gh calls with a timeout and a stderr capture so silent failures become diagnosable.
- Cache gh JSON output to a file so reruns can use --input after transient failures.
When it happens
Trigger: gh pr list / gh api invoked by fetch_live_prs or _fetch_live_pr_files failing with empty stderr: expired token (gh auth status shows logged out), network unreachable so gh dies early, gh not installed and a shim exiting oddly, or SIGKILL/OOM producing a bare non-zero rc.
Common situations: Long-lived audit job whose gh token expired between runs; running in CI where gh is present but GH_TOKEN env is missing/invalid; corporate proxy blocking api.github.com; gh version mismatch after brew upgrade.
Related errors
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/4265bf36fa6d9b92.
Report an issue: GitHub.