{"record":{"id":"35051e2871966703","repo":"pbakaus/impeccable","slug":"gh-args-join-failed-with-exit-result-sta","errorCode":null,"errorMessage":"gh ${args.join(' ')} failed with exit ${result.status}: ${result.stderr || result.stdout}","messagePattern":"gh (.+?) failed with exit (.+?): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/github/sheriff.mjs","lineNumber":787,"sourceCode":"function runGhJson(args) {\n  const result = runGh(args, { quiet: true });\n  try {\n    return JSON.parse(result.stdout || '{}');\n  } catch (err) {\n    throw new Error(`Failed to parse gh JSON output: ${err.message}`);\n  }\n}\n\nfunction runGh(args, options = {}) {\n  const result = spawnSync('gh', args, {\n    encoding: 'utf-8',\n    env: process.env,\n  });\n  if (!options.quiet && result.stdout) process.stdout.write(result.stdout);\n  if (!options.quiet && result.stderr) process.stderr.write(result.stderr);\n  if (result.error) throw result.error;\n  if (result.status !== 0 && !options.allowFailure) {\n    throw new Error(`gh ${args.join(' ')} failed with exit ${result.status}: ${result.stderr || result.stdout}`);\n  }\n  return result;\n}\n\nfunction printHelp() {\n  console.log(`Usage: node scripts/github/sheriff.mjs [--repo owner/name] [--apply]\n\nDefault mode is a dry run. The scheduled workflow runs with:\n  --apply --warning-days 7 --close-days 14\n\nOptions:\n  --apply                         mutate labels, comments, and stale PR state\n  --dry-run                       print changes without mutating GitHub\n  --repo owner/name               repository to inspect (defaults to GITHUB_REPOSITORY)\n  --warning-days n                warn waiting PRs after n days open (default: 7)\n  --close-days n                  close waiting PRs after n days open (default: 14)\n  --maintainers a,b               maintainer logins allowed to use /sheriff wait\n  --regular-contributors a,b      contributors exempt from auto-close unless --auto-close-regulars is set","sourceCodeStart":769,"sourceCodeEnd":805,"githubUrl":"https://github.com/pbakaus/impeccable/blob/d14711ae3d1a1dd62dee61a358d27f107c51ccd0/scripts/github/sheriff.mjs#L769-L805","documentation":"Thrown by runGh in the GitHub sheriff script when a spawned `gh` command exits with a non-zero status (and allowFailure is not set). The message embeds the full gh argument list, the exit code, and stderr (falling back to stdout) so the underlying gh failure is visible. This is the primary surface for any gh-level failure: auth errors, rate limits, missing repos, and permission denials all bubble up here.","triggerScenarios":"Expired or missing GITHUB_TOKEN / gh auth; querying a repo the token cannot read; hitting GitHub primary or secondary rate limits; gh not installed (though that surfaces as result.error first); a branch-protection or permission rejection on a mutate subcommand; --repo pointing at a typo'd owner/name.","commonSituations":"A scheduled stale-PR workflow whose token expired; running sheriff locally without `gh auth login`; a fork where the actor lacks write access; GitHub secondary rate limits kicking in during a bulk label/close sweep; CI image missing the gh binary.","solutions":["Run `gh auth status` and re-authenticate with `gh auth login` or by setting a valid GITHUB_TOKEN.","Copy the exact gh args from the error message and run them manually to reproduce the real gh error.","Verify --repo owner/name is correct and that the token has the needed scopes (repo / read:org).","If rate-limited, wait and retry; for secondary limits, reduce concurrency or batch size in the sheriff config.","Pass { allowFailure: true } to runGh for non-critical calls where a gh failure should not abort the whole run."],"exampleFix":"// before\nconst result = runGh(['pr', 'list', '--repo', repo]);\n\n// after: preflight auth so the failure is diagnosed before the sweep\nconst authed = runGh(['auth', 'status'], { allowFailure: true, quiet: true });\nif (authed.status !== 0) {\n  throw new Error('gh is not authenticated; run `gh auth login` before sheriff');\n}\nconst result = runGh(['pr', 'list', '--repo', repo]);","handlingStrategy":"try-catch","validationCode":"// Preflight: gh installed + authenticated before the sweep starts.\nimport { spawnSync } from 'node:child_process';\nfunction ghReady() {\n  const r = spawnSync('gh', ['auth', 'status'], { encoding: 'utf-8' });\n  return r.status === 0;\n}","typeGuard":null,"tryCatchPattern":"// Wrap runGh so non-zero exits include a hint and allow opt-in continuation.\nfunction runGhSafe(args, { allowFailure = false } = {}) {\n  try {\n    return runGh(args, { quiet: true, allowFailure });\n  } catch (err) {\n    const msg = String(err.message || err);\n    if (/rate limit/i.test(msg)) throw new Error('GitHub rate limit hit; back off and retry.');\n    if (/authentication|credentials|token/i.test(msg)) throw new Error('gh auth invalid; run `gh auth login`.');\n    throw err;\n  }\n}","preventionTips":["Run `gh auth status` before any sweep and fail fast with a clear message.","Pass allowFailure:true for non-critical gh calls so one failure does not abort everything.","Log the exact gh args on failure so the command is reproducible by hand.","Keep the GITHUB_TOKEN fresh in scheduled workflows; tokens expire."],"tags":["github","cli","auth","network","spawn","rate-limit"],"backgroundTag":null,"analyzedSha":"d14711ae3d1a1dd62dee61a358d27f107c51ccd0","analyzedAt":"2026-08-13T00:52:25.771Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}