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
- Inspect the parsed payload (log it) to see the actual structure.
- Make the parser emit a bare top-level array, or wrap the list under {jobs: [...]} / {results: [...]}.
- 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
- Standardize every local parser to emit a bare top-level JSON array.
- If you need an envelope, use exactly the key 'jobs' or 'results'.
- Add a parser-output schema check in CI by running each parser against a fixture and validating the shape.
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
- landingjobs: unexpected API response — expected a JSON array
- local parser returned invalid JSON
- echojobs: unexpected API response on page ${page} — expected
- manfred: unexpected API response — expected a JSON array of
- nofluffjobs: unexpected API response — expected { postings:
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/8946eac71a1e4a11.
Report an issue: GitHub.