santifer/career-ops · error · Error
getonbrd: unexpected API response for category "${category}"
Error message
getonbrd: unexpected API response for category "${category}" on page ${page} — expected { data: [...] }, got keys: [${json ? Object.keys(json).join(', ') : 'null'}] What it means
The getonbrd provider fetch loop calls fetchJson on the Getonbrd API per category/page and expects a JSON envelope of shape { data: [...] }. If the response is null, not an object, or lacks an array `data`, the provider throws this error listing the keys actually received. It signals the API contract changed, the endpoint returned an error payload, or a non-JSON/empty body was parsed.
Source
Thrown at providers/getonbrd.mjs:182
async fetch(entry, ctx) {
const categories = resolveCategories(entry);
const maxPages = resolveMaxPages(entry);
const fallbackCompany = entry?.name;
const out = [];
// A posting can appear under several categories; first sighting wins so the
// scanner never sees the same URL twice from one entry.
const seen = new Set();
for (const category of categories) {
const base = assertGetonbrdUrl(feedBase(category));
for (let page = 1; page <= maxPages; page++) {
const url = `${base}?per_page=${PER_PAGE}&expand[]=company&page=${page}`;
// redirect:'error' prevents SSRF via server-side redirects
const json = await ctx.fetchJson(url, { redirect: 'error' });
if (!json || !Array.isArray(json.data)) {
throw new Error(
`getonbrd: unexpected API response for category "${category}" on page ${page} — expected { data: [...] }, got keys: [${json ? Object.keys(json).join(', ') : 'null'}]`,
);
}
for (const j of json.data) {
const normalized = normalizeGetonbrdJob(j, fallbackCompany);
if (!normalized || seen.has(normalized.url)) continue;
seen.add(normalized.url);
out.push(normalized);
}
if (json.data.length < PER_PAGE) break; // short page → last page reached
}
}
return out;
},
};
View on GitHub (pinned to 1696bec4d0)
Solutions
- Read the 'got keys' list in the message — it tells you the actual response shape and usually identifies the new envelope key.
- Verify the category slug is valid by opening the API URL manually in a browser/curl.
- Check whether Getonbrd changed its API response format and update normalize/fetch code accordingly.
- Retry after a delay if a transient server issue is suspected; add rate-limit backoff if calls are too aggressive.
- Confirm network middleware isn't returning an HTML error page or empty body (proxy, VPN, corporate firewall).
Example fix
// before
const json = await ctx.fetchJson(url, { redirect: 'error' });
if (!json || !Array.isArray(json.data)) throw new Error(`...`);
// after
const json = await ctx.fetchJson(url, { redirect: 'error' });
const items = json?.data ?? json?.jobs ?? json?.results;
if (!Array.isArray(items)) throw new Error(`getonbrd: unexpected response keys: ${Object.keys(json || {}).join(',')}`); Defensive patterns
Strategy: type-guard
Validate before calling
const res = await fetch(url);
const json = await res.json();
if (res.ok && json && typeof json === 'object' && Array.isArray(json.data)) {
// safe to proceed
} Type guard
function isGetonbrdPage(json) {
return typeof json === 'object' && json !== null && Array.isArray(json.data);
} Try / catch
try {
const json = await ctx.fetchJson(url, { redirect: 'error' });
if (!isGetonbrdPage(json)) {
console.warn(`getonbrd: non-standard response for ${category} page ${page}:`, Object.keys(json || {}));
break; // stop paging, keep earlier pages' results
}
} catch (e) {
if (e.message.includes('unexpected API response')) {
await sleep(retryDelayMs);
continue; // retry once for transient issues
}
throw e;
} Prevention
- Check the 'got keys' output against the live API whenever Getonbrd ships changes.
- Add a smoke test that hits one real category/page and asserts the { data: [] } shape.
- Use per_page pagination carefully and stop when data is empty rather than over-requesting.
- Wrap fetchJson with retry/backoff for transient 5xx/empty bodies.
When it happens
Trigger: fetchJson returns null (empty/204 body), an error object like { error: '...' }, a paginated envelope with a different key (e.g. { jobs: [...] }), or an HTML error page that fails to parse upstream — for any page within 1..maxPages of a category fetch.
Common situations: Getonbrd API version change relocating the array; rate-limit or auth responses shaped differently; category slug no longer exists so the API returns an error payload; transient 5xx rendered as a non-standard body; `expand[]=company` parameter change.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- remoteok: unexpected API response — expected a JSON array, g
- remotive: unexpected API response — expected { jobs: [...] }
- remotli: unexpected API response — expected { jobs: [...] },
- arbeitnow: unexpected API response on page ${page} — expecte
- jobicy: unexpected API response — expected { jobs: [...] },
AI-assisted analysis of santifer/career-ops@1696bec4d0 (2026-09-01).
Data as JSON: /api/errors/d4a22be8ece7f88c.
Report an issue: GitHub.