santifer/career-ops · error · Error
solidjobs: unexpected API response — expected
Error message
solidjobs: unexpected API response — expected { jobs: [...] }, got keys: [${json ? Object.keys(json).join(', ') : 'null'}] What it means
After fetching, the provider asserts the response is a JSON object with a jobs array (expected shape { jobs: [...] }). If the JSON is null, not an object, or lacks an array-valued jobs key, it throws, embedding the actual top-level keys (or 'null') to aid debugging. This catches API drift, HTML error pages parsed as junk, and proxy/auth walls.
Solutions
- Log the printed key list to see what the endpoint actually returned and adapt
- Confirm the careers_url points at the offers API (not another solidjobs endpoint)
- Check whether solid.jobs changed its public API contract and update the provider's parsing
- Retry later if it is a transient rate-limit/WAF response
Example fix
// endpoint drifted
// before: { jobs: [...] }
// after: { data: [...] } — update provider to read json.data || json.jobs
const jobs = json.jobs ?? json.data; Defensive patterns
Strategy: try-catch
Validate before calling
null
Type guard
function hasExpectedShape(json) {
return !!json && typeof json === 'object' && Array.isArray(json.jobs);
} Try / catch
try {
offers = await provider.fetch(entry, ctx);
} catch (e) {
if (e.message.includes('unexpected API response')) {
console.error(`${entry.name}: solidjobs API shape changed or was blocked — inspect keys in message`);
return [];
}
throw e;
} Prevention
- Read the key list in the message — it tells you exactly what the endpoint returned
- Pin/bookmark the API contract and watch for solidjobs API changes
- Check for WAF/rate-limit responses when failures cluster in time
- Write the provider's expected schema into portals.yml comments for future editors
When it happens
Trigger: solid.jobs changes its API contract; a Cloudflare/auth wall returns JSON like { error: 'forbidden' }; fetchJson returns null on an empty body; an HTML error page fails JSON parsing upstream and json arrives null.
Common situations: Endpoint works but a newer API version returns { data: [...] } or { offers: [...] }; rate limiting returning a JSON error object; misconfigured careers_url hitting a different endpoint that returns valid JSON of the wrong shape.
Related errors
- Apify did not return a run id
- getonbrd: unexpected API response for category
- glints: unexpected API response
- hackernews: unexpected item response for thread
- remoteok: unexpected API response — expected a JSON array…
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/f3a70f6a8c0ce409.
Report an issue: GitHub.
Appendix: source
Thrown at providers/solidjobs.mjs:67
return { url };
} catch {}
return null;
},
/**
* Fetches and normalizes job offers from the SolidJobs public API.
* * @param {{ careers_url?: string, name: string }} entry - The configuration entry being processed.
* @param {{ fetchJson: (url: string, opts?: { redirect?: 'error'|'follow'|'manual' }) => Promise<any> }} ctx - HTTP context.
* @returns {Promise<Array<{title: string, url: string, company: string, location: string}>>} Array of parsed job offers.
*/
async fetch(entry, ctx) {
const url = entry.careers_url;
if (!url) throw new Error('solidjobs: careers_url required');
assertUrl(url);
// redirect:'error' prevents SSRF via server-side redirects
const json = await ctx.fetchJson(url, { redirect: 'error' });
if (!json || !Array.isArray(json.jobs)) {
throw new Error(`solidjobs: unexpected API response — expected { jobs: [...] }, got keys: [${json ? Object.keys(json).join(', ') : 'null'}]`);
}
/** @type {Array<{ title?: string, url?: string, company?: string, locations?: string | string[] }>} */
const jobs = json.jobs;
return jobs
.filter(j => j && typeof j === 'object' && typeof j.url === 'string' && j.url.trim() !== '')
.map(j => ({
title: j.title || '',
url: /** @type {string} */ (j.url || '').trim(),
company: j.company || entry.name,
location: Array.isArray(j.locations) ? j.locations.join(', ') : (typeof j.locations === 'string' ? j.locations : ''),
}));
},
};
View on GitHub (pinned to aac998c7ed)