{"record":{"id":"4265bf36fa6d9b92","repo":"odysseus-dev/odysseus","slug":"cmd-0-exited-with-result-returncode","errorCode":null,"errorMessage":"{cmd[0]} exited with {result.returncode}","messagePattern":"(.+?) exited with (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"scripts/pr_blocker_audit.py","lineNumber":255,"sourceCode":"    if isinstance(payload, dict):\n        if warnings:\n            payload[\"warnings\"] = [*payload.get(\"warnings\", []), *warnings]\n        return payload\n    if warnings:\n        return {\"items\": payload, \"warnings\": warnings}\n    return payload\n\n\ndef _fetch_live_pr_files(repo: str, number: int) -> list[str]:\n    api_path = f\"repos/{repo}/pulls/{number}/files?per_page=100\"\n    payload = _run_gh_json([\"gh\", \"api\", \"--paginate\", api_path])\n    return _extract_files(payload)\n\n\ndef _run_gh_json(cmd: list[str]):\n    result = subprocess.run(cmd, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False)\n    if result.returncode != 0:\n        raise RuntimeError(result.stderr.strip() or f\"{cmd[0]} exited with {result.returncode}\")\n    try:\n        return json.loads(result.stdout or \"[]\")\n    except json.JSONDecodeError as exc:\n        raise RuntimeError(f\"gh returned invalid JSON: {exc}\") from exc\n\n\ndef normalize_prs(payload) -> list[PullRequest]:\n    raw_prs = payload.get(\"items\", []) if isinstance(payload, dict) else payload\n    if raw_prs is None:\n        raw_prs = []\n    if not isinstance(raw_prs, list):\n        raise ValueError(\"expected input JSON to be a list of pull requests or an object with an items list\")\n    return [normalize_pr(item) for item in raw_prs if isinstance(item, dict)]\n\n\ndef missing_file_metadata_count(prs: list[PullRequest]) -> int:\n    return sum(1 for pr in prs if not pr.files)\n","sourceCodeStart":237,"sourceCodeEnd":273,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/scripts/pr_blocker_audit.py#L237-L273","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\n$ python scripts/pr_blocker_audit.py --repo org/repo --live\nRuntimeError: gh exited with 1\n\n# after\n$ gh auth status          # find expired/missing credential\n$ gh auth login            # or: export GH_TOKEN=ghp_xxx\n$ python scripts/pr_blocker_audit.py --repo org/repo --live","handlingStrategy":"retry","validationCode":"import subprocess\n\ndef gh_ready() -> bool:\n    try:\n        r = subprocess.run(['gh', 'auth', 'status'], capture_output=True, text=True, timeout=15)\n        return r.returncode == 0\n    except (OSError, subprocess.TimeoutExpired):\n        return False","typeGuard":"import shutil, subprocess\n\ndef gh_usable() -> bool:\n    if not shutil.which('gh'):\n        return False\n    return gh_ready()","tryCatchPattern":"for attempt in range(3):\n    try:\n        prs = fetch_live_prs(repo)\n        break\n    except RuntimeError as e:\n        msg = str(e)\n        if 'exited with' in msg and attempt < 2:\n            time.sleep(2 ** attempt)  # transient gh failure (auth expiry is NOT transient: fix gh auth)\n            continue\n        raise","preventionTips":["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."],"tags":["github-cli","subprocess","authentication","network","cli"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}