affaan-m/ECC · error · Error
${displayCommand} returned invalid JSON: ${error.message}
Error message
${displayCommand} returned invalid JSON: ${error.message} What it means
Thrown by scripts/runGhJson in scripts/work-items.js when gh exits 0 but its stdout cannot be parsed as JSON (JSON.parse throws). The function explicitly requests JSON output via `--json ...` flags, so valid JSON is expected; a parse failure indicates gh produced unexpected text — most often because a gh extension, alias, or shell wrapper intercepted the command and emitted human-readable or prefixed output instead of raw JSON.
Source
Thrown at scripts/work-items.js:166
const commandArgs = shimPath ? [shimPath, ...args] : args;
const displayCommand = shimPath ? `node ${shimPath} ${args.join(' ')}` : `gh ${args.join(' ')}`;
const result = spawnSync(command, commandArgs, {
encoding: 'utf8',
maxBuffer: 10 * 1024 * 1024
});
if (result.error) {
throw new Error(`Failed to run gh: ${result.error.message}`);
}
if (result.status !== 0) {
throw new Error(`${displayCommand} failed: ${(result.stderr || result.stdout || '').trim()}`);
}
try {
return JSON.parse(result.stdout || '[]');
} catch (error) {
throw new Error(`${displayCommand} returned invalid JSON: ${error.message}`);
}
}
function slugifyWorkItemSegment(value) {
return (
String(value || '')
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '') || 'unknown'
);
}
function githubWorkItemId(repo, type, number) {
return `github-${slugifyWorkItemSegment(repo)}-${type}-${number}`;
}
function githubPrStatus(pr) {
if (pr.isDraft || pr.mergeStateStatus === 'DIRTY') {View on GitHub (pinned to 01e15490f0)
Solutions
- Run the underlying gh command manually (`gh pr list --repo owner/repo --json number,title,...`) and inspect stdout to see what non-JSON content is present.
- Disable or bypass gh aliases/extensions that alter list output (check `gh alias list` and installed extensions).
- If using ECC_GH_SHIM, ensure the shim prints only the JSON array to stdout (send any logging to stderr).
- Confirm the gh version supports the requested --json fields.
Example fix
// before # gh alias 'pr' prepends a banner line, breaking JSON.parse node scripts/work-items.js sync-github --repo owner/repo // after gh alias delete pr node scripts/work-items.js sync-github --repo owner/repo
Defensive patterns
Strategy: validation
Validate before calling
const { spawnSync } = require('child_process');
function ghEmitsCleanJson(repo) {
const r = spawnSync('gh', ['pr', 'list', '--repo', repo, '--state', 'open', '--limit', '1', '--json', 'number'], { encoding: 'utf8' });
if (r.status !== 0) return false;
try { JSON.parse(r.stdout); return true; } catch { return false; }
}
if (!ghEmitsCleanJson('owner/repo')) {
throw new Error('gh is not emitting clean JSON — check aliases/extensions (gh alias list)');
} Type guard
function isJsonArray(stdout) {
try { const v = JSON.parse(stdout); return Array.isArray(v); } catch { return false; }
} Try / catch
// When invalid JSON is seen, fall back to a direct GitHub API call or skip the sync
try {
runSync(['sync-github', '--repo', repo]);
} catch (e) {
if (/invalid JSON/.test(e.message)) {
console.warn('gh produced non-JSON output; check gh aliases/extensions. Skipping sync.');
} else {
throw e;
}
} Prevention
- Periodically run `gh alias list` and `gh extension list` to spot output-modifying add-ons.
- If using ECC_GH_SHIM, ensure it writes only JSON to stdout and logs to stderr.
- Pin the gh version in CI so the JSON schema does not change unexpectedly.
- Smoke-test `gh pr list --json number` output parses before relying on sync-github.
When it happens
Trigger: A gh extension or alias shadowing `pr list` / `issue list` that prints a header line before the JSON; a gh version that changes field names or output shape; stdout contaminated by a shell rc file or a gh plugin banner; ECC_GH_SHIM returning non-JSON output.
Common situations: Users with gh extensions installed that modify default output; a custom gh wrapper script set via ECC_GH_SHIM that does not emit clean JSON; a gh version downgrade/upgrade that altered the JSON schema.
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
- Failed to run gh: ${result.error.message}
- ${displayCommand} failed: ${(result.stderr || result.stdout
- Invalid --metadata-json: ${error.message}
- ${flagName} requires a value
- Unknown argument: ${arg}
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/27c7efc9fbe36990.
Report an issue: GitHub.