{"record":{"id":"831627542835c775","repo":"affaan-m/ECC","slug":"gh-args-join-returned-invalid-json-erro-831627","errorCode":null,"errorMessage":"gh ${args.join(' ')} returned invalid JSON: ${error.message}","messagePattern":"gh (.+?) returned invalid JSON: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/platform-audit.js","lineNumber":258,"sourceCode":"\n  return result.stdout || '';\n}\n\nfunction runGhJson(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.useEnvGithubToken) {\n    delete env.GITHUB_TOKEN;\n  }\n\n  const stdout = runCommand(command, commandArgs, { env });\n  try {\n    return JSON.parse(stdout || 'null');\n  } catch (error) {\n    throw new Error(`gh ${args.join(' ')} returned invalid JSON: ${error.message}`);\n  }\n}\n\nfunction readText(rootDir, relativePath) {\n  try {\n    return fs.readFileSync(path.join(rootDir, relativePath), 'utf8');\n  } catch (_error) {\n    return '';\n  }\n}\n\nfunction fileExists(rootDir, relativePath) {\n  return fs.existsSync(path.join(rootDir, relativePath));\n}\n\nfunction safeParseJson(text) {\n  if (!text || !text.trim()) {\n    return null;","sourceCodeStart":240,"sourceCodeEnd":276,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/platform-audit.js#L240-L276","documentation":"runGhJson wraps runCommand and expects the `gh` CLI's stdout to be valid JSON; if JSON.parse throws, this error is raised, embedding the parse error message and the gh argument list. It guards against gh printing warnings, auth prompts, or human-readable errors into stdout that then corrupt the expected JSON payload.","triggerScenarios":"gh emitting non-JSON output on stdout (auth prompts, update notices, HTML error pages) while still exiting 0; a truncated or empty-plus-garbage response; piping gh output through filters that modify it.","commonSituations":"gh session prompting for authentication interactively; gh version mismatch printing upgrade banners; enterprise proxies injecting content; gh's `--jq` filter returning a non-JSON scalar unexpectedly mixed with text.","solutions":["Check what `gh <args>` actually prints on stdout and fix the cause (re-auth, upgrade gh)","Ensure gh runs non-interactively (GH_TOKEN set, no TTY prompts)","Upgrade or align the gh CLI version in the environment","Wrap parse output with a sanity check that gh produced JSON (e.g. try `gh ... --json` manually)"],"exampleFix":"// before\nconst data = runGhJson(['pr', 'list']);\n// after\nconst raw = runGhJson(['pr', 'list']);\nconst data = Array.isArray(raw) ? raw : [];\nconsole.error('gh returned unexpected payload, continuing with empty list');","handlingStrategy":"type-guard","validationCode":"function looksLikeJson(stdout) {\n  const s = (stdout || '').trim();\n  return s.startsWith('{') || s.startsWith('[') || s === 'null';\n}","typeGuard":"function isParsedArray(value) {\n  return Array.isArray(value);\n}","tryCatchPattern":"try {\n  const data = runGhJson(['pr', 'list']);\n} catch (e) {\n  if (e.message.includes('returned invalid JSON')) {\n    console.error('gh stdout was not JSON — check auth prompts or gh version banners:', e.message);\n    process.exitCode = 1;\n  } else throw e;\n}","preventionTips":["Set GH_TOKEN / CI env so gh never prompts interactively","Keep gh CLI updated and consistent across environments","Redirect gh banners/warnings away from stdout when possible","Sanity-check raw gh output before parsing in wrapper scripts"],"tags":["json","subprocess","gh-cli"],"backgroundTag":"json-parse-error","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}