santifer/career-ops · error · Error
remoteok: unexpected API response — expected a JSON array…
Error message
remoteok: unexpected API response — expected a JSON array, got ${data === null ? 'null' : typeof data} What it means
RemoteOK's public feed (FEED_URL) is expected to return a top-level JSON array of job objects. fetch() throws this error when the parsed response is not an array (null, object, string, etc.), treating it as an unexpected API change or an error page that still parsed as JSON.
Solutions
- Retry after a delay — transient rate-limit or CDN responses are the usual cause
- Inspect the raw response from the RemoteOK feed URL to see the actual payload
- Check whether RemoteOK changed its API shape and update the provider accordingly
- Reduce scan frequency to avoid triggering rate limiting
Example fix
null
Defensive patterns
Strategy: try-catch
Validate before calling
null
Type guard
function isArrayResponse(v) { return Array.isArray(v); } Try / catch
try {
const jobs = await remoteok.fetch(entry, ctx);
} catch (err) {
if (err.message.startsWith('remoteok: unexpected API response')) {
console.warn('RemoteOK feed returned a non-array payload — likely rate-limited or schema change; retrying later');
return [];
}
throw err;
} Prevention
- Space out scans to avoid RemoteOK rate limiting
- Pin/monitor the feed shape; RemoteOK occasionally changes its response envelope
- Fallback to another aggregator provider when RemoteOK is down
When it happens
Trigger: The RemoteOK API returns an HTML error/rate-limit page that fetchJson parses oddly, a JSON object wrapper, null body, or any non-array payload; API schema changes or Cloudflare interstitials.
Common situations: RemoteOK rate-limits or temporarily takes the feed down; a CDN returns a JSON error object; RemoteOK changes its response envelope from an array to an 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
- remotive: unexpected API response — expected
- 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/288cd494e4fc9b1d.
Report an issue: GitHub.
Appendix: source
Thrown at providers/remoteok.mjs:29
// private scanning, but don't redistribute this feed publicly without it.
const FEED_URL = 'https://remoteok.com/api';
/** @type {Provider} */
export default {
id: 'remoteok',
/**
* Fetches and normalizes postings from the RemoteOK 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 data = await ctx.fetchJson(FEED_URL, { redirect: 'error' });
if (!Array.isArray(data)) {
throw new Error(`remoteok: unexpected API response — expected a JSON array, got ${data === null ? 'null' : typeof data}`);
}
return data
.filter(j => j && typeof j === 'object'
&& typeof j.position === 'string' && j.position.trim() !== ''
&& typeof j.url === 'string' && /^https?:\/\//i.test(j.url.trim()))
.map(j => ({
title: j.position.trim(),
url: j.url.trim(),
company: typeof j.company === 'string' && j.company.trim() ? j.company.trim() : (entry.name || 'RemoteOK'),
location: typeof j.location === 'string' ? j.location.trim() : '',
}));
},
};
View on GitHub (pinned to aac998c7ed)