{"record":{"id":"41f62207c271be73","repo":"tinyhumansai/openhuman","slug":"gh-failed-r-status-r-stderr-trim-un","errorCode":null,"errorMessage":"gh failed (${r.status}): ${r.stderr?.trim() || \"unknown error\"}","messagePattern":"gh failed \\((.+?)\\): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/agent-batch/status.mjs","lineNumber":74,"sourceCode":"function fetchPrsFromGh(spec) {\n  // One `gh pr list` call per batch — cheap and avoids N+1.\n  const args = [\n    \"pr\",\n    \"list\",\n    \"--repo\",\n    spec.base_repo,\n    \"--state\",\n    \"all\",\n    \"--search\",\n    `label:batch:${spec.batch_id}`,\n    \"--json\",\n    \"headRefName,number,url,state,statusCheckRollup\",\n    \"--limit\",\n    \"100\",\n  ];\n  const r = spawnSync(\"gh\", args, { encoding: \"utf8\" });\n  if (r.status !== 0) {\n    throw new Error(\n      `gh failed (${r.status}): ${r.stderr?.trim() || \"unknown error\"}`,\n    );\n  }\n  return JSON.parse(r.stdout || \"[]\");\n}\n\nfunction indexByBranch(prs) {\n  const m = new Map();\n  for (const pr of prs) {\n    // `statusCheckRollup` from gh is an array of contexts when populated; we\n    // only care about the worst-status rollup for the cell.\n    let rollup = null;\n    if (\n      Array.isArray(pr.statusCheckRollup) &&\n      pr.statusCheckRollup.length > 0\n    ) {\n      const states = pr.statusCheckRollup\n        .map((c) => c.conclusion || c.state)","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/scripts/agent-batch/status.mjs#L56-L92","documentation":"fetchPrsFromGh() in scripts/agent-batch/status.mjs shells out to `gh pr list --repo <base_repo> --search label:batch:<batch_id> --json ...` via spawnSync and throws when the child exits non-zero, embedding gh's trimmed stderr. This is the single PR-discovery call the status table is built from, so any gh/CLI/auth/network failure surfaces here.","triggerScenarios":"Running `node scripts/agent-batch/status.mjs <spec.json>` (or --post) when: gh is not installed (spawnSync yields status null and the message reads 'gh failed (null): unknown error'), `gh auth status` shows no login, GH_TOKEN is expired/revoked, there is no network egress to api.github.com, or the repo tinyhumansai/openhuman is unreachable. Also fires on any gh CLI flag/API incompatibility after a gh version change.","commonSituations":"Running the batch tooling in a fresh container or CI runner without gh installed/authenticated; corporate proxies blocking api.github.com; a revoked PAT used via GH_TOKEN; gh major-version upgrades that change pr list flags. Note validateSpec() pins base_repo to tinyhumansai/openhuman, so a wrong-repo typo fails earlier with a SpecError, not here.","solutions":["Run `gh auth status` — if not logged in, `gh auth login` or export a valid GH_TOKEN/GITHUB_TOKEN","Verify gh is on PATH (`command -v gh`); install it if missing — status null in the message means the binary was not found","Check network/proxy reachability: `gh pr list --repo tinyhumansai/openhuman --limit 1` should succeed standalone","Re-run the status command once transient network/rate-limit conditions clear (the message carries gh's own stderr for diagnosis)"],"exampleFix":"# before\n$ node scripts/agent-batch/status.mjs batch.json\nError: gh failed (1): gh auth not logged in\n\n# after\n$ gh auth login\n$ node scripts/agent-batch/status.mjs batch.json","handlingStrategy":"retry","validationCode":"// Pre-flight before invoking status.mjs\nimport { spawnSync } from \"node:child_process\";\nconst auth = spawnSync(\"gh\", [\"auth\", \"status\"], { encoding: \"utf8\" });\nif (auth.status !== 0) throw new Error(\"gh not authenticated — run `gh auth login`\");\nconst probe = spawnSync(\"gh\", [\"pr\", \"list\", \"--repo\", \"tinyhumansai/openhuman\", \"--limit\", \"1\"], { encoding: \"utf8\" });\nif (probe.status !== 0) throw new Error(`gh unreachable: ${probe.stderr}`);","typeGuard":null,"tryCatchPattern":"function fetchPrsSafe(spec, attempts = 3) {\n  for (let i = 0; i < attempts; i++) {\n    const r = spawnSync(\"gh\", prListArgs(spec), { encoding: \"utf8\" });\n    if (r.status === 0) return JSON.parse(r.stdout || \"[]\");\n    if (r.error || /rate limit|timed out|ECONNRESET/i.test(r.stderr || \"\")) {\n      await sleep(5000 * (i + 1));\n      continue;\n    }\n    throw new Error(`gh failed (${r.status}): ${r.stderr}`); // auth/repo errors are not transient\n  }\n  throw new Error(\"gh pr list kept failing after retries\");\n}","preventionTips":["Run `gh auth status` before any batch tooling in CI and fail fast with a clear message","Distinguish r.error (binary missing / spawn failure) from r.status !== 0 (gh ran and failed) — the former means install gh, the latter means read stderr","Wrap invocations of status.mjs in a retry-with-backoff for network blips, but never retry auth failures blindly"],"tags":["github","cli","network","auth"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}