thedotmack/claude-mem · error · Error

Could not parse ${label} JSON: ${message}

Error message

Could not parse ${label} JSON: ${message}

What it means

Thrown by parseJson() when JSON.parse fails on output returned by runGh(). It labels which logical payload failed ('pull request', 'checks', 'reviews', etc.) so the caller knows which gh call produced unparseable text. This guards every JSON-expecting gh result in the script.

Source

Thrown at scripts/pr-babysit-status.ts:124

  }
}

function runGh(args: string[], options: { allowExitCodes?: number[] } = {}): string {
  const result = runCommand(['gh', ...args]);
  const allowed = new Set([0, ...(options.allowExitCodes ?? [])]);
  if (!allowed.has(result.exitCode)) {
    const detail = result.stderr || result.stdout || `exit code ${result.exitCode}`;
    throw new Error(`gh ${args.join(' ')} failed: ${detail}`);
  }
  return result.stdout;
}

function parseJson<T>(raw: string, label: string): T {
  try {
    return JSON.parse(raw) as T;
  } catch (error) {
    const message = error instanceof Error ? error.message : String(error);
    throw new Error(`Could not parse ${label} JSON: ${message}`);
  }
}

function checkPrerequisites() {
  const git = runCommand(['git', 'rev-parse', '--is-inside-work-tree']);
  if (git.exitCode !== 0 || git.stdout.trim() !== 'true') {
    throw new Error('Not in a git repository. Run this from a checked-out repo.');
  }

  const ghVersion = runCommand(['gh', '--version']);
  if (ghVersion.exitCode !== 0) {
    throw new Error('GitHub CLI is not available. Install gh and try again.');
  }

  const auth = runCommand(['gh', 'auth', 'status']);
  if (auth.exitCode !== 0) {
    throw new Error(`GitHub CLI is not authenticated. Run "gh auth login".\n${auth.stderr || auth.stdout}`.trim());
  }

View on GitHub (pinned to d768ba3643)

Solutions

  1. Reproduce by running the exact gh command (shown via the label and the failing call site) and inspect its raw stdout.
  2. Suppress gh noise: ensure GH_NO_UPDATE_NOTIFIER or run with a quiet/known gh version.
  3. If stdout is empty, guard parseJson callers to skip when raw is falsy (the checks path already does `raw ? parseJson(...) : []`).
  4. If pagination is the cause, switch from --paginate to an explicit loop with a per-page parse.

Example fix

// before — always parse even when gh printed nothing
return parseJson<CheckRun[]>(runGh([...]), 'checks')
// after — skip parse on empty output
const raw = runGh([...], { allowExitCodes: [GH_PENDING_EXIT_CODE] })
return raw ? parseJson<CheckRun[]>(raw, 'checks') : []
Defensive patterns

Strategy: validation

Validate before calling

function tryParse<T>(raw: string, label: string): T | null {
  try { return JSON.parse(raw) as T; } catch { return null; }
}
// skip when raw is empty
const data = raw ? tryJson<T>(raw, 'checks') : [];

Type guard

function looksLikeJson(s: string): boolean {
  const t = s.trim();
  return t.startsWith('{') || t.startsWith('[');
}

Try / catch

try {
  return parseJson<T>(raw, label);
} catch (e) {
  if (e instanceof Error && /Could not parse/.test(e.message)) {
    console.error(`gh produced non-JSON for ${label}:`, raw.slice(0, 200));
  }
  throw e;
}

Prevention

When it happens

Trigger: gh emitted a non-JSON string before its JSON (e.g. a warning on stderr merged into stdout, a deprecation notice, an interactive prompt). An empty stdout where parseJson was still called. A gh version that changes the --json shape. A network glitch truncating paginated output mid-array.

Common situations: gh updated to a version that prepends a banner. gh api --paginate interleaving records in a way that isn't a clean JSON array. Running in an environment where gh prints update notifications to stdout. A repo so large that paginated reviews exceed memory and produce partial output.

Related errors


AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12). Data as JSON: /api/errors/4a3d60da40a9847a. Report an issue: GitHub.