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
- Check what `gh <args>` actually prints on stdout and fix the cause (re-auth, upgrade gh)
- Ensure gh runs non-interactively (GH_TOKEN set, no TTY prompts)
- Upgrade or align the gh CLI version in the environment
- 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
- Set GH_TOKEN / CI env so gh never prompts interactively
- Keep gh CLI updated and consistent across environments
- Redirect gh banners/warnings away from stdout when possible
- Sanity-check raw gh output before parsing in wrapper scripts
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.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- gh returned invalid JSON
- application request must be a JSON object
- blender rendered nothing
- bundle values must be finite JSON data
- candidate configuration exceeds 1 MiB
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)