santifer/career-ops · error · Error

local parser JSON must be an array or contain jobs[]/results

Error message

local parser JSON must be an array or contain jobs[]/results[]

What it means

JSON.parse succeeded but the parsed value is neither an array nor an object containing a jobs or results array. The provider accepts a bare array (payload) or an envelope ({jobs:[...]} or {results:[...]}); anything else is rejected so the downstream .map() always receives an array.

Source

Thrown at providers/local-parser.mjs:182

  // cwd is pinned to the project root so a relative script arg resolves to the
  // same file resolveInvocation() validated, regardless of the caller's cwd.
  const { stdout } = await execFileAsync(command, args, {
    cwd: PROJECT_ROOT,
    timeout,
    maxBuffer,
    windowsHide: true,
  });

  let payload;
  try {
    payload = JSON.parse(stdout);
  } catch {
    throw new Error('local parser returned invalid JSON');
  }

  const rawJobs = Array.isArray(payload) ? payload : payload.jobs || payload.results;
  if (!Array.isArray(rawJobs)) {
    throw new Error('local parser JSON must be an array or contain jobs[]/results[]');
  }

  return rawJobs
    .map(job => normalizeParserJob(job, entry))
    .filter(Boolean);
}

/** @type {Provider} */
export default {
  id: 'local-parser',

  detect(entry) {
    if (!entry.parser?.command) return null;

    // An invocation we can't safely resolve (unknown command, out-of-repo or
    // missing script, inline-code flags) is not runnable — skip it.
    try {
      resolveInvocation(entry);

View on GitHub (pinned to 9b17a8ac97)

Solutions

  1. Inspect the parsed payload (log it) to see the actual structure.
  2. Make the parser emit a bare top-level array, or wrap the list under {jobs: [...]} / {results: [...]}.
  3. If the existing key is different (e.g. 'offers'), change the parser to rename it to 'jobs', or extend the provider's rawJobs extraction line.

Example fix

# before (parser stdout)
{"offers": [{"title": "Dev"}]}

# after (parser renamed the key)
{"jobs": [{"title": "Dev"}]}
Defensive patterns

Strategy: type-guard

Validate before calling

// Confirm the parser output contract: array, or object with jobs[]/results[].
function conformsToParserContract(payload) {
  if (Array.isArray(payload)) return true;
  if (payload && typeof payload === 'object') return Array.isArray(payload.jobs) || Array.isArray(payload.results);
  return false;
}

Type guard

/** @param {unknown} payload */
function isParserJobList(payload) {
  if (Array.isArray(payload)) return true;
  if (payload && typeof payload === 'object') {
    const o = /** @type {any} */ (payload);
    return Array.isArray(o.jobs) || Array.isArray(o.results);
  }
  return false;
}

Try / catch

try {
  const jobs = await provider.fetch(entry, ctx);
  results.push(...jobs);
} catch (err) {
  if (err.message.includes('must be an array or contain jobs[]/results[]')) {
    console.warn(`parser ${entry.name} returned an unexpected JSON shape — emit a bare array or {jobs:[]}/{results:[]}`);
  }
  throw err;
}

Prevention

When it happens

Trigger: The parser emitted valid JSON of an unexpected shape: a single job object {}, an object whose list lives under a different key (e.g. {offers:[...]}), a scalar (string/number), or {jobs: {not: 'an array'}}.

Common situations: The parser's output key isn't 'jobs' or 'results'; the parser returns one job object instead of a list; the parser schema changed and the array was renamed; a wrapper object was added around the list.

Related errors


AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13). Data as JSON: /api/errors/8946eac71a1e4a11. Report an issue: GitHub.