santifer/career-ops · error · Error
${provider.id}: fetch() did not return an array
Error message
${provider.id}: fetch() did not return an array What it means
After a provider's fetch() resolves, scan.mjs verifies Array.isArray(jobs) and throws a plain Error if not. Each ATS provider (Greenhouse, Lever, Ashby, local parser, etc.) is contractually required to return an array of job objects; a non-array return is a provider-contract violation indicating a bug in the provider adapter or an unexpected API response shape.
Source
Thrown at scan.mjs:2420
let sourceName = provider.id === 'local-parser' ? 'local-parser' : `${provider.id}-api`;
try {
let jobs;
try {
jobs = await provider.fetch(company, ctx);
} catch (parserErr) {
if (provider.id !== 'local-parser') throw parserErr;
const fallback = resolveProvider(company, providers, { skipIds: ['local-parser'] });
if (!fallback || fallback.error) throw parserErr;
provider = fallback.provider;
sourceName = `${provider.id}-api`;
jobs = await provider.fetch(company, ctx);
errors.push({
company: company.name,
error: `local parser failed, used API fallback: ${parserErr.message}`,
});
}
if (!Array.isArray(jobs)) {
throw new Error(`${provider.id}: fetch() did not return an array`);
}
totalFound += jobs.length;
if (!company._isBoard && jobs.length === 0) {
emptyTargets.push(company.name);
}
for (const job of jobs) {
// Trust enrichment — runs before filters, never drops
const trustResult = trustValidator(job);
job.trustScore = trustResult.score;
job.trustFlags = trustResult.flags;
job.trustLevel = trustResult.level;
// Company blacklist (#1742) — the user's own do-not-apply decision,
// checked first: it's company-level, not a per-posting signal. Never
// silent: skips are counted and reported in the run summary, and
// --include-blacklisted lets the posting through annotated instead.
if (blacklist.size > 0) {View on GitHub (pinned to 9b17a8ac97)
Solutions
- Inspect the provider adapter for the named provider.id — ensure it returns an array (even if empty).
- If the upstream API envelope changed, normalize in the adapter: return payload.results || payload.jobs || [].
- Guard before the throw: coerce jobs = Array.isArray(jobs) ? jobs : (jobs ? [jobs] : []).
- Check the provider's error path — a swallowed exception returning undefined is a likely culprit.
Example fix
// before (provider adapter)
async fetch(company, ctx) {
return await response.json(); // returns { jobs: [...] }
}
// after
async fetch(company, ctx) {
const payload = await response.json();
return Array.isArray(payload) ? payload : (payload.jobs || []);
} Defensive patterns
Strategy: type-guard
Validate before calling
const jobs = await provider.fetch(company, ctx);
if (!Array.isArray(jobs)) {
throw new TypeError(`${provider.id}: expected array, got ${typeof jobs}`);
} Type guard
function isJobsArray(result) {
return Array.isArray(result) && result.every(j => j && typeof j === 'object');
} Try / catch
let jobs = await provider.fetch(company, ctx);
if (!Array.isArray(jobs)) {
console.warn(`${provider.id} returned non-array (${typeof jobs}); coercing`);
jobs = Array.isArray(jobs?.jobs) ? jobs.jobs : (jobs ? [jobs] : []);
} Prevention
- Enforce Array.isArray in every provider adapter's return contract via tests.
- Monitor upstream ATS API changelogs for response-envelope changes.
- Add a contract test that asserts each provider returns an array for a fixture response.
When it happens
Trigger: A provider returns null, undefined, an object, or a string instead of an array; an upstream ATS API changes its response envelope (e.g. wrapping jobs in {results: [...]}) and the adapter forwards it untransformed; a provider throws and a fallback returns a non-array.
Common situations: ATS vendor changes their API response format; a custom/local provider adapter that returns a single object for a one-job posting; a fetch that returns the raw Response object instead of parsed JSON.
Related errors
- a16z-speedrun-talent: invalid URL: ${url}
- a16z-speedrun-talent: URL must use HTTPS: ${url}
- a16z-speedrun-talent: untrusted hostname "${parsed.hostname}
- a16z-speedrun-talent: unexpected API response on page ${page
- agentic-jobs: invalid URL: ${url}
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/a5e444d3235b3715.
Report an issue: GitHub.