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

  1. 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.
  2. Disable or bypass gh aliases/extensions that alter list output (check `gh alias list` and installed extensions).
  3. If using ECC_GH_SHIM, ensure the shim prints only the JSON array to stdout (send any logging to stderr).
  4. 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

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

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/27c7efc9fbe36990. Report an issue: GitHub.