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

  1. Run the exact `gh ... --json` command manually and inspect its raw output.
  2. Upgrade the GitHub CLI to a version supporting all requested --json fields.
  3. Fix or remove a custom ECC_GH_SHIM so it emits only JSON on stdout.
  4. 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

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.

Related errors


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)