affaan-m/ECC · error
gh returned invalid JSON
Error message
gh ${args.join(' ')} returned invalid JSON: ${error.message} What it means
runGhJson parses the stdout of a gh command as JSON and wraps any parse failure (or thrown error from runGh) in a message indicating the gh output was not valid JSON. The library expects `gh --json ...` to always emit parseable JSON, so non-JSON output signals an unexpected gh behavior, an error banner mixed into stdout, or a shim producing human-readable text.
Solutions
- Run the exact `gh ... --json` command manually and inspect its raw output.
- Upgrade the GitHub CLI to a version supporting all requested --json fields.
- Fix or remove a custom ECC_GH_SHIM so it emits only JSON on stdout.
- Re-authenticate gh so it doesn't emit interactive prompts into the output stream.
Example fix
// before const json = runGhJson(['issue', 'view', '123', '-R', 'o/r', '--json', 'newField']); // after // after upgrading gh to a version supporting 'newField' const json = runGhJson(['issue', 'view', '123', '-R', 'o/r', '--json', 'number,title,state,newField']);
Defensive patterns
Strategy: try-catch
Validate before calling
const raw = runGh(['issue', 'view', String(n), '-R', repo, '--json', 'number']);
try { JSON.parse(raw); } catch (e) { throw new Error(`gh output is not JSON: ${raw.slice(0, 100)}`); } Type guard
const isJsonObject = (v) => v !== null && typeof v === 'object' && !Array.isArray(v);
Try / catch
try {
const json = runGhJson(args);
} catch (e) {
if (e.message.includes('returned invalid JSON')) {
console.error('gh emitted non-JSON output; check gh version and ECC_GH_SHIM stdout purity:', e.message);
} else { throw e; }
} Prevention
- Keep gh CLI up to date so all --json fields are supported.
- Ensure custom shims print ONLY JSON to stdout; logs go to stderr.
- Avoid environments where gh writes prompts/banners to stdout.
- Test JSON output of gh commands manually when upgrading.
When it happens
Trigger: Calling runGhJson (via json, getIssue, or listIssues) when gh prints a non-JSON error/warning to stdout, when a custom ECC_GH_SHIM echoes plain text, or when gh output is empty/unexpected for the requested fields.
Common situations: Outdated gh versions that don't support a requested --json field, a shim script that prints logging text before the JSON, locale/encoding issues corrupting output, or gh auth prompts being written to stdout.
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Cannot read JSON object
- gh returned invalid JSON
- gh returned invalid JSON
- Malformed coordination JSON in body
- Memory frontmatter field in
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/1a948fa93a8285f0.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/github-coordination/gh-api.js:78
// privileges.
function runGh(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.stripGithubToken) {
delete env.GITHUB_TOKEN;
}
return runCommand(command, commandArgs, { cwd: options.cwd, env });
}
function runGhJson(args, options = {}) {
try {
return JSON.parse(runGh(args, options) || 'null');
} catch (error) {
throw new Error(`gh ${args.join(' ')} returned invalid JSON: ${error.message}`);
}
}
function getIssue(repo, issueNumber, options = {}) {
const { owner, name } = normalizeRepo(repo);
const json = runGhJson([
'issue',
'view',
String(issueNumber),
'--repo',
`${owner}/${name}`,
'--json',
'number,title,body,url,state,labels,author,updatedAt,assignees',
], options);
if (!json) {
throw new Error(`Unable to load issue #${issueNumber} from ${repo}`);
}View on GitHub (pinned to 8321021c54)