affaan-m/ECC · error

gh returned invalid JSON

Error message

gh ${args.join(' ')} returned invalid JSON: ${error.message}

What it means

runGhJson wraps runCommand and expects the `gh` CLI's stdout to be valid JSON; if JSON.parse throws, this error is raised, embedding the parse error message and the gh argument list. It guards against gh printing warnings, auth prompts, or human-readable errors into stdout that then corrupt the expected JSON payload.

Solutions

  1. Check what `gh <args>` actually prints on stdout and fix the cause (re-auth, upgrade gh)
  2. Ensure gh runs non-interactively (GH_TOKEN set, no TTY prompts)
  3. Upgrade or align the gh CLI version in the environment
  4. Wrap parse output with a sanity check that gh produced JSON (e.g. try `gh ... --json` manually)

Example fix

// before
const data = runGhJson(['pr', 'list']);
// after
const raw = runGhJson(['pr', 'list']);
const data = Array.isArray(raw) ? raw : [];
console.error('gh returned unexpected payload, continuing with empty list');
Defensive patterns

Strategy: type-guard

Validate before calling

function looksLikeJson(stdout) {
  const s = (stdout || '').trim();
  return s.startsWith('{') || s.startsWith('[') || s === 'null';
}

Type guard

function isParsedArray(value) {
  return Array.isArray(value);
}

Try / catch

try {
  const data = runGhJson(['pr', 'list']);
} catch (e) {
  if (e.message.includes('returned invalid JSON')) {
    console.error('gh stdout was not JSON — check auth prompts or gh version banners:', e.message);
    process.exitCode = 1;
  } else throw e;
}

Prevention

When it happens

Trigger: gh emitting non-JSON output on stdout (auth prompts, update notices, HTML error pages) while still exiting 0; a truncated or empty-plus-garbage response; piping gh output through filters that modify it.

Common situations: gh session prompting for authentication interactively; gh version mismatch printing upgrade banners; enterprise proxies injecting content; gh's `--jq` filter returning a non-JSON scalar unexpectedly mixed with text.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/831627542835c775. Report an issue: GitHub.

Appendix: source

Thrown at scripts/platform-audit.js:258

  return result.stdout || '';
}

function runGhJson(args, options = {}) {
  const shimPath = process.env.ECC_GH_SHIM;
  const command = shimPath ? process.execPath : 'gh';
  const commandArgs = shimPath ? [shimPath, ...args] : args;
  const env = { ...process.env };

  if (!options.useEnvGithubToken) {
    delete env.GITHUB_TOKEN;
  }

  const stdout = runCommand(command, commandArgs, { env });
  try {
    return JSON.parse(stdout || 'null');
  } catch (error) {
    throw new Error(`gh ${args.join(' ')} returned invalid JSON: ${error.message}`);
  }
}

function readText(rootDir, relativePath) {
  try {
    return fs.readFileSync(path.join(rootDir, relativePath), 'utf8');
  } catch (_error) {
    return '';
  }
}

function fileExists(rootDir, relativePath) {
  return fs.existsSync(path.join(rootDir, relativePath));
}

function safeParseJson(text) {
  if (!text || !text.trim()) {
    return null;

View on GitHub (pinned to 8321021c54)