{"record":{"id":"1a948fa93a8285f0","repo":"affaan-m/ECC","slug":"gh-args-join-returned-invalid-json-erro","errorCode":null,"errorMessage":"gh ${args.join(' ')} returned invalid JSON: ${error.message}","messagePattern":"gh (.+?) returned invalid JSON: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/github-coordination/gh-api.js","lineNumber":78,"sourceCode":"// privileges.\nfunction runGh(args, options = {}) {\n  const shimPath = process.env.ECC_GH_SHIM;\n  const command = shimPath ? process.execPath : 'gh';\n  const commandArgs = shimPath ? [shimPath, ...args] : args;\n  const env = { ...process.env };\n\n  if (options.stripGithubToken) {\n    delete env.GITHUB_TOKEN;\n  }\n\n  return runCommand(command, commandArgs, { cwd: options.cwd, env });\n}\n\nfunction runGhJson(args, options = {}) {\n  try {\n    return JSON.parse(runGh(args, options) || 'null');\n  } catch (error) {\n    throw new Error(`gh ${args.join(' ')} returned invalid JSON: ${error.message}`);\n  }\n}\n\nfunction getIssue(repo, issueNumber, options = {}) {\n  const { owner, name } = normalizeRepo(repo);\n  const json = runGhJson([\n    'issue',\n    'view',\n    String(issueNumber),\n    '--repo',\n    `${owner}/${name}`,\n    '--json',\n    'number,title,body,url,state,labels,author,updatedAt,assignees',\n  ], options);\n\n  if (!json) {\n    throw new Error(`Unable to load issue #${issueNumber} from ${repo}`);\n  }","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/lib/github-coordination/gh-api.js#L60-L96","documentation":"Thrown by runGhJson when runGh succeeds (exit 0) but the stdout is not valid JSON. runGhJson wraps JSON.parse around the gh output; a parse failure means gh returned non-JSON text — usually a warning on stderr that leaked to stdout, a missing --json flag, a gh version that prints human text, or a shim returning wrong output.","triggerScenarios":"A gh invocation missing the --json flag (so it prints a table); gh printing a deprecation/notice before the JSON; a shim (ECC_GH_SHIM) returning non-JSON; a gh version whose JSON output shape differs.","commonSituations":"gh auto-updated and changed output; a wrapper/alias injects text; the shim path is set but returns plain text; network proxy mangled the response; stderr redirect merged into stdout.","solutions":["Inspect the raw stdout (log runGh output before parsing) to see what non-JSON text was returned.","Ensure every gh call that feeds runGhJson includes the --json <fields> flag.","If using ECC_GH_SHIM, make the shim emit valid JSON on stdout and warnings on stderr only.","Pin a known-good gh version or add a version check, and separate stderr so notices don't corrupt stdout."],"exampleFix":"// before\nconst out = runGhJson(['issue', 'view', String(n)]); // missing --json\n\n// after — always pass --json and validate\nconst out = runGhJson(['issue', 'view', String(n), '--json', 'number,title,body']);\n// inside runGhJson, surface the raw text on parse failure:\n// catch (e) { throw new Error(`bad JSON: ${raw.slice(0,200)}`); }","handlingStrategy":"try-catch","validationCode":"function looksJson(s) { const t = String(s || '').trim(); return t.startsWith('{') || t.startsWith('['); }\nconst raw = runGh(args);\nif (!looksJson(raw)) {\n  throw new Error(`gh did not return JSON (first 200 chars): ${raw.slice(0,200)}`);\n}","typeGuard":"function isParsableJson(s) {\n  try { JSON.parse(s); return true; } catch { return false; }\n}","tryCatchPattern":"try {\n  runGhJson(args);\n} catch (e) {\n  if (/invalid JSON/.test(e.message)) { console.error('gh output was not JSON — check the --json flag and shim output'); throw e; }\n  throw e;\n}","preventionTips":["Always pass --json <fields> to gh calls feeding runGhJson.","Keep shim output strictly JSON on stdout; send notices to stderr.","Pin a known gh version and test its JSON output shape."],"tags":["github-coordination","gh-api","json","parsing","shim"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}