pbakaus/impeccable · error · Error
Failed to parse gh JSON output: ${err.message}
Error message
Failed to parse gh JSON output: ${err.message} What it means
Thrown by runGhJson in the GitHub sheriff script after the `gh` CLI exited successfully (status 0) but emitted stdout that is not valid JSON. runGhJson runs `gh`, then calls JSON.parse(result.stdout || '{}'); any SyntaxError from parse is rewrapped with this message. Because runGh already validated a zero exit code, this specifically means gh produced non-JSON text despite succeeding.
Source
Thrown at scripts/github/sheriff.mjs:774
return new Set(logins.map(normalizeLogin).filter(Boolean));
}
function normalizeLogin(login) {
return String(login || '').toLowerCase();
}
function requireValue(argv, index, flag) {
const value = argv[index];
if (!value || value.startsWith('--')) throw new Error(`${flag} requires a value.`);
return value;
}
function runGhJson(args) {
const result = runGh(args, { quiet: true });
try {
return JSON.parse(result.stdout || '{}');
} catch (err) {
throw new Error(`Failed to parse gh JSON output: ${err.message}`);
}
}
function runGh(args, options = {}) {
const result = spawnSync('gh', args, {
encoding: 'utf-8',
env: process.env,
});
if (!options.quiet && result.stdout) process.stdout.write(result.stdout);
if (!options.quiet && result.stderr) process.stderr.write(result.stderr);
if (result.error) throw result.error;
if (result.status !== 0 && !options.allowFailure) {
throw new Error(`gh ${args.join(' ')} failed with exit ${result.status}: ${result.stderr || result.stdout}`);
}
return result;
}
function printHelp() {View on GitHub (pinned to d14711ae3d)
Solutions
- Inspect the raw stdout: temporarily log result.stdout before the JSON.parse to see exactly what gh returned.
- Confirm every gh call routed through runGhJson passes an explicit --json <field,...> flag so gh emits machine-readable output.
- Run `gh auth status` and `gh api user` to verify auth is healthy and stderr (not stdout) carries warnings.
- Pin or upgrade gh to a known version and check the GitHub CLI changelog for output-format changes.
- If a proxy/Enterprise host is involved, verify GH_HOST and HTTP(S)_PROXY env vars are not redirecting stdout.
Example fix
// before
function runGhJson(args) {
const result = runGh(args, { quiet: true });
return JSON.parse(result.stdout || '{}');
}
// after: assert JSON was requested and surface the raw payload on failure
function runGhJson(args) {
if (!args.some(a => a === '--json' || a.startsWith('--jq'))) {
throw new Error(`runGhJson requires a --json flag; got: ${args.join(' ')}`);
}
const result = runGh(args, { quiet: true });
try {
return JSON.parse(result.stdout || '{}');
} catch (err) {
throw new Error(`Failed to parse gh JSON output: ${err.message} (raw stdout: ${String(result.stdout).slice(0, 200)})`);
}
} Defensive patterns
Strategy: validation
Validate before calling
// Before calling runGhJson, assert the args request JSON and sniff the output.
function assertJsonArgs(args) {
const i = args.indexOf('--json');
if (i === -1 || i === args.length - 1) {
throw new Error('runGhJson requires --json <fields>');
}
}
// After runGh, sanity-check stdout is object/array-shaped before parse:
function looksJsony(s) {
const t = String(s ?? '').trimStart();
return t.startsWith('{') || t.startsWith('[');
} Try / catch
// Catch parse failures distinctly from gh failures so each is diagnosable.
function safeGhJson(args) {
let result;
try {
result = runGh(args, { quiet: true });
} catch (ghErr) {
throw new Error(`gh invocation failed: ${ghErr.message}`);
}
if (!looksJsony(result.stdout)) {
throw new Error(`gh returned non-JSON stdout: ${String(result.stdout).slice(0, 120)}`);
}
return JSON.parse(result.stdout);
} Prevention
- Always pass --json <fields> to gh calls that feed JSON.parse.
- Pin the gh CLI version in CI so output format cannot drift between runs.
- Assert stdout starts with { or [ before parsing so a parse error becomes a diagnosable shape error.
- Keep gh auth healthy with a preflight `gh auth status` before the sweep.
When it happens
Trigger: Calling runGhJson with gh args that do not request JSON output (missing --json <fields>), gh prepending an auth/network warning to stdout, a `gh` version that prints a deprecation notice on stdout, a transparent proxy or GH_HOST override returning an HTML error page with a 200, or stdout being empty/non-text.
Common situations: gh auth warnings leaking onto stdout instead of stderr; a gh subcommand that defaults to human-readable tables when no --json flag is passed; GitHub Enterprise returning an interstitial page; a CI runner with a stale gh version whose output format changed between minor releases.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- gh ${args.join(' ')} failed with exit ${result.status}: ${re
- Missing repository. Pass --repo owner/name or set GITHUB_REP
- Unknown ignore-rule flag: ${arg}
- Pass a rule id, e.g. ${IMPECCABLE_COMMAND} hooks ignore-rule
- overused-font is value-specific by default. Use ${IMPECCABLE
AI-assisted analysis of pbakaus/impeccable@d14711ae3d (2026-08-13).
Data as JSON: /api/errors/87d716b342f2c1d8.
Report an issue: GitHub.