santifer/career-ops · error · Error
landingjobs: unexpected API response — expected a JSON array
Error message
landingjobs: unexpected API response — expected a JSON array, got ${json === null ? 'null' : typeof json} What it means
The LandingJobs provider fetches a JSON feed (FEED_URL) and requires the top-level value to be an array of job postings. This error fires after fetchJson succeeds (HTTP response parsed as JSON) but the result is not an Array — it reports the actual type received (or 'null'). It guards the downstream .map().filter() chain, which would otherwise throw a less informative TypeError.
Source
Thrown at providers/landingjobs.mjs:125
const location = [base, j.remote === true ? 'Remote' : ''].filter(Boolean).join(', ');
/** @type {{ title: string, url: string, company: string, location: string, postedAt?: number }} */
const job = { title, url, company, location };
const postedAt = toEpochMs(j.published_at) ?? toEpochMs(j.created_at);
if (postedAt !== undefined) job.postedAt = postedAt;
return job;
}
/** @type {Provider} */
export default {
id: 'landingjobs',
async fetch(entry, ctx) {
assertLandingUrl(FEED_URL);
// redirect:'error' prevents SSRF via server-side redirects
const json = await ctx.fetchJson(FEED_URL, { redirect: 'error' });
if (!Array.isArray(json)) {
throw new Error(
`landingjobs: unexpected API response — expected a JSON array, got ${json === null ? 'null' : typeof json}`,
);
}
const fallbackCompany = entry?.name;
return json.map(j => normalizeLandingJob(j, fallbackCompany)).filter(Boolean);
},
};
View on GitHub (pinned to 9b17a8ac97)
Solutions
- Log the received value (console.log(json) before the throw) to see the actual shape LandingJobs returned.
- If the API now wraps jobs in an envelope, unwrap it: replace the Array.isArray check with `const arr = Array.isArray(json) ? json : json?.jobs || json?.data; if(!Array.isArray(arr)) throw ...`.
- Verify FEED_URL still points at the documented feed endpoint by curl-ing it directly.
- If null/object corresponds to a known rate-limit or maintenance response, surface it as a distinct, retriable error instead of a hard failure.
Example fix
// before
if (!Array.isArray(json)) {
throw new Error(`landingjobs: unexpected API response — expected a JSON array, got ${json === null ? 'null' : typeof json}`);
}
return json.map(j => normalizeLandingJob(j, fallbackCompany)).filter(Boolean);
// after — tolerate a common envelope shape
const arr = Array.isArray(json) ? json : (json && (Array.isArray(json.jobs) ? json.jobs : Array.isArray(json.data) ? json.data : null));
if (!Array.isArray(arr)) {
throw new Error(`landingjobs: unexpected API response — expected a JSON array or {jobs|data:[]}, got ${json === null ? 'null' : typeof json}`);
}
return arr.map(j => normalizeLandingJob(j, fallbackCompany)).filter(Boolean); Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-check the response shape before relying on the provider's own guard.
// Useful when you call ctx.fetchJson directly in a custom integration.
function isLandingJobsFeed(value) {
return Array.isArray(value);
}
// const json = await ctx.fetchJson(url, { redirect: 'error' });
// if (!isLandingJobsFeed(json)) { /* log + skip, or unwrap envelope */ } Type guard
/** @param {unknown} v */
function isJobArray(v) {
return Array.isArray(v) && v.every(item => item && typeof item === 'object');
} Try / catch
// At the scan/orchestrator level, isolate each provider so one bad
// response shape never aborts the whole sweep.
try {
const jobs = await provider.fetch(entry, ctx);
results.push(...jobs);
} catch (err) {
console.error(`[skip] ${provider.id} (${entry.name}): ${err.message}`);
// continue with the next provider — do not rethrow for shape mismatches
} Prevention
- Pin FEED_URL to the documented LandingJobs feed endpoint and treat changes to it as a code review item.
- Log the raw json (type + keys) before the throw so a response-shape change is diagnosable in one run.
- Wrap every provider.fetch() at the orchestrator in try/catch and continue past failures.
- If LandingJobs is known to envelope results, normalize once in normalizeLandingJob's caller rather than relying on a bare array.
When it happens
Trigger: ctx.fetchJson(FEED_URL) returns a JSON object (e.g. an envelope like {data:[...]} or {jobs:[...]}), returns null, returns a single job object, or returns a maintenance/error payload such as {error:'rate limited'}. The template literal embeds json===null?'null':typeof json so the message distinguishes null from object/string.
Common situations: LandingJobs changes its feed response shape (wraps results in an envelope); a temporary outage returns a JSON error body instead of the feed; the FEED_URL constant was edited to point at a non-feed endpoint; a proxy/CDN injects a JSON status object.
Related errors
- local parser JSON must be an array or contain jobs[]/results
- echojobs: unexpected API response on page ${page} — expected
- manfred: unexpected API response — expected a JSON array of
- nofluffjobs: unexpected API response — expected { postings:
- remoteok: unexpected API response — expected a JSON array, g
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/986c998ff0290920.
Report an issue: GitHub.