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

  1. Inspect the raw stdout: temporarily log result.stdout before the JSON.parse to see exactly what gh returned.
  2. Confirm every gh call routed through runGhJson passes an explicit --json <field,...> flag so gh emits machine-readable output.
  3. Run `gh auth status` and `gh api user` to verify auth is healthy and stderr (not stdout) carries warnings.
  4. Pin or upgrade gh to a known version and check the GitHub CLI changelog for output-format changes.
  5. 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

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

Related errors


AI-assisted analysis of pbakaus/impeccable@d14711ae3d (2026-08-13). Data as JSON: /api/errors/87d716b342f2c1d8. Report an issue: GitHub.