santifer/career-ops · error · Error
remotive: unexpected API response — expected
Error message
remotive: unexpected API response — expected { jobs: [...] }, got keys: [${json ? Object.keys(json).join(', ') : 'null'}] What it means
Remotive's public feed must return a JSON object with a `jobs` array ({ jobs: [...] }). fetch() throws this error when the response is null, not an object, or lacks a jobs array. The message includes the observed top-level keys (or 'null') to aid debugging.
Solutions
- Retry after a delay; transient error bodies are common
- Fetch the Remotive feed URL manually and inspect the top-level keys shown in the error message
- Check Remotive API docs/changelog for an envelope change (e.g. jobs renamed or nested under data) and update the provider parser
- Verify no proxy/middleware is rewriting the response body
Example fix
null
Defensive patterns
Strategy: try-catch
Validate before calling
null
Type guard
function hasJobsArray(json) {
return json !== null && typeof json === 'object' && Array.isArray(json.jobs);
} Try / catch
try {
const jobs = await remotive.fetch(entry, ctx);
} catch (err) {
if (err.message.startsWith('remotive: unexpected API response')) {
console.warn(`Remotive envelope changed or errored (${err.message}) — check feed manually`);
return [];
}
throw err;
} Prevention
- Check the error's keys list first — it tells you exactly what envelope came back
- Watch Remotive API changelogs for jobs-key renames or nesting changes
- Add a health probe that calls the feed and asserts { jobs: [...] } before scheduled scans
When it happens
Trigger: The Remotive API returns null/empty body, a bare array, a JSON error object without `jobs`, or changes its envelope; rate limiting or CDN error pages that still parse as JSON.
Common situations: Remotive deploys an API version change moving `jobs` under another key; a proxy returns a JSON error payload; transient outage where the feed returns an error object.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
- remoteok: unexpected API response — expected a JSON array…
- All active models failed. Last error
- arbeitsagentur: all keyword request(s) failed
- archive render skipped — Greenhouse API unreachable
- himalayas: unexpected API response - expected
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/06d9547434b3e161.
Report an issue: GitHub.
Appendix: source
Thrown at providers/remotive.mjs:28
// Wire in via a `job_boards:` entry with `provider: remotive`.
const FEED_URL = 'https://remotive.com/api/remote-jobs';
/** @type {Provider} */
export default {
id: 'remotive',
/**
* Fetches and normalizes postings from the Remotive public feed.
* @param {{ name?: string }} entry - The job_boards 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}>>}
*/
async fetch(entry, ctx) {
// redirect:'error' prevents SSRF via server-side redirects
const json = await ctx.fetchJson(FEED_URL, { redirect: 'error' });
if (!json || !Array.isArray(json.jobs)) {
throw new Error(`remotive: unexpected API response — expected { jobs: [...] }, got keys: [${json ? Object.keys(json).join(', ') : 'null'}]`);
}
return json.jobs
.filter(j => j && typeof j === 'object'
&& typeof j.title === 'string' && j.title.trim() !== ''
&& typeof j.url === 'string' && /^https?:\/\//i.test(j.url.trim()))
.map(j => ({
title: j.title.trim(),
url: j.url.trim(),
company: typeof j.company_name === 'string' && j.company_name.trim() ? j.company_name.trim() : (entry.name || 'Remotive'),
location: typeof j.candidate_required_location === 'string' ? j.candidate_required_location.trim() : '',
}));
},
};
View on GitHub (pinned to aac998c7ed)