{"record":{"id":"8946eac71a1e4a11","repo":"santifer/career-ops","slug":"local-parser-json-must-be-an-array-or-contain-jobs","errorCode":null,"errorMessage":"local parser JSON must be an array or contain jobs[]/results[]","messagePattern":"local parser JSON must be an array or contain jobs\\[\\]/results\\[\\]","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/local-parser.mjs","lineNumber":182,"sourceCode":"  // cwd is pinned to the project root so a relative script arg resolves to the\n  // same file resolveInvocation() validated, regardless of the caller's cwd.\n  const { stdout } = await execFileAsync(command, args, {\n    cwd: PROJECT_ROOT,\n    timeout,\n    maxBuffer,\n    windowsHide: true,\n  });\n\n  let payload;\n  try {\n    payload = JSON.parse(stdout);\n  } catch {\n    throw new Error('local parser returned invalid JSON');\n  }\n\n  const rawJobs = Array.isArray(payload) ? payload : payload.jobs || payload.results;\n  if (!Array.isArray(rawJobs)) {\n    throw new Error('local parser JSON must be an array or contain jobs[]/results[]');\n  }\n\n  return rawJobs\n    .map(job => normalizeParserJob(job, entry))\n    .filter(Boolean);\n}\n\n/** @type {Provider} */\nexport default {\n  id: 'local-parser',\n\n  detect(entry) {\n    if (!entry.parser?.command) return null;\n\n    // An invocation we can't safely resolve (unknown command, out-of-repo or\n    // missing script, inline-code flags) is not runnable — skip it.\n    try {\n      resolveInvocation(entry);","sourceCodeStart":164,"sourceCodeEnd":200,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/providers/local-parser.mjs#L164-L200","documentation":"After parsing stdout as JSON, runLocalParser() requires the payload to be a top-level array or an object with a jobs[] or results[] array. Any other shape (a bare object of metadata, a {data: ...} wrapper, a single job object) is rejected so the provider never silently yields zero jobs from a misinterpreted payload.","triggerScenarios":"Parser prints valid JSON whose shape is unsupported — e.g. {\"data\": [...]}, {\"postings\": [...]}, or a single {\"title\":..., \"url\":...} object — so payload.jobs and payload.results are both undefined and the Array.isArray check fails.","commonSituations":"Parser written for a different consumer expecting {data: [...]}; API response passed through verbatim including an envelope (count/next/data); emitting one JSON object instead of an array when only one job exists; key renamed from jobs to openings in a parser refactor.","solutions":["Change the parser to emit a top-level array, or wrap the list under jobs or results: console.log(JSON.stringify({jobs: raw})) ","If the upstream API uses a `data` envelope, unwrap it in the parser before printing.","When only one job is found, still emit an array ([job]) not the bare object.","As a consumer, catch this error and inspect the parser's actual output shape before changing config — it signals a contract mismatch between parser script and provider, not a network failure."],"exampleFix":"// before (parser script)\nconsole.log(JSON.stringify({ data: jobs }));\n// after\nconsole.log(JSON.stringify({ jobs: jobs }));","handlingStrategy":"type-guard","validationCode":"const payload = JSON.parse(stdout);\nconst ok = Array.isArray(payload) || Array.isArray(payload?.jobs) || Array.isArray(payload?.results);\nif (!ok) throw new Error('parser output must be [] or {jobs:[…]}/{results:[…]}');","typeGuard":"function isParserPayload(p) {\n  return Array.isArray(p) || Array.isArray(p?.jobs) || Array.isArray(p?.results);\n}","tryCatchPattern":"try {\n  const jobs = await localParser.fetch(entry);\n} catch (e) {\n  if (String(e.message).includes('must be an array or contain jobs[]/results[]')) {\n    console.error(`${entry.name}: unwrap the upstream envelope ({data:[…]}) into {jobs:[…]} in the parser`);\n    return [];\n  }\n  throw e;\n}","preventionTips":["Agree on the payload contract (array or jobs[]/results[]) for every parser script and document it in the script header.","Unwrap API envelopes (data/postings) inside the parser before printing.","Always emit an array even for a single job.","Add a unit test per parser asserting isParserPayload(JSON.parse(output))."],"tags":["json","schema","parser"],"backgroundTag":"unexpected-response-shape","analyzedSha":"aac998c7ed7248ea853b720ceeb1fdbeb322fc5d","analyzedAt":"2026-09-16T06:35:29.214Z","contentChangedAt":"2026-09-16T06:35:29.214Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}