santifer/career-ops · error · Error
workingnomads: unexpected API response — expected a JSON arr
Error message
workingnomads: unexpected API response — expected a JSON array, got ${data === null ? 'null' : typeof data} What it means
Working Nomads' feed is expected to be a top-level JSON array. This throws when ctx.fetchJson returned something that is not an array — an object (error envelope), null, or a primitive — surfacing the actual type in the message.
Source
Thrown at providers/workingnomads.mjs:26
// Wire in via a `job_boards:` entry with `provider: workingnomads`.
const FEED_URL = 'https://www.workingnomads.com/api/exposed_jobs/';
/** @type {Provider} */
export default {
id: 'workingnomads',
/**
* Fetches and normalizes postings from the Working Nomads 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(`workingnomads: unexpected API response — expected a JSON array, got ${data === null ? 'null' : typeof data}`);
}
return data
.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 || 'Working Nomads'),
location: typeof j.location === 'string' ? j.location.trim() : '',
}));
},
};
View on GitHub (pinned to 9b17a8ac97)
Solutions
- Fetch the URL directly to see the current shape: curl -s https://www.workingnomads.com/api/exposed_jobs/ | head -c 500.
- If the shape changed permanently, update workingnomads.mjs parsing to match the new contract (e.g. unwrap a {jobs: [...]} envelope).
- If it is an error envelope, retry later — likely transient.
- If the feed moved, update FEED_URL.
Example fix
// before
const data = await ctx.fetchJson(FEED_URL, { redirect: "error" });
if (!Array.isArray(data)) throw new Error(`workingnomads: unexpected API response ...`);
// after — tolerate a wrapped envelope if Working Nomads changes shape
const data = await ctx.fetchJson(FEED_URL, { redirect: "error" });
const rows = Array.isArray(data) ? data : Array.isArray(data?.jobs) ? data.jobs : null;
if (!rows) throw new Error(`workingnomads: unexpected API response — got ${data === null ? "null" : typeof data}`); Defensive patterns
Strategy: try-catch
Validate before calling
// probe the feed shape out-of-band before relying on it
const probe = await fetch('https://www.workingnomads.com/api/exposed_jobs/').then(r => r.json());
if (!Array.isArray(probe)) console.warn("workingnomads feed shape changed:", typeof probe); Type guard
const isJobArray = (d) => Array.isArray(d) && d.every(j => j && typeof j === "object" && typeof j.title === "string" && typeof j.url === "string");
Try / catch
try {
jobs = await workingnomads.fetch(entry, ctx);
} catch (err) {
if (/workingnomads: unexpected API response/.test(err.message)) {
logApiContractChange("workingnomads", err.message);
continue; // skip this provider, keep scanning others
}
throw err;
} Prevention
- Treat this error as an upstream-contract-change signal and alert on it.
- Add a smoke test that asserts the feed is an array.
- Pin the provider version; review when Working Nomads announces API changes.
When it happens
Trigger: The endpoint https://www.workingnomads.com/api/exposed_jobs/ returned a JSON object instead of an array (contract change or error body), returned null, or an upstream proxy returned a JSON error envelope. A zero-length array [ ] does NOT trigger this — only a non-array top-level value does.
Common situations: Working Nomads changed their API shape; the endpoint is temporarily returning an error object {error: ...}; a CDN returned a JSON-formatted error; the feed URL moved.
Related errors
- payload must include at least one of: cv, articleDigest
- arbeitnow: unexpected API response on page ${page} — expecte
- echojobs: unexpected API response on page ${page} — expected
- remoteok: unexpected API response — expected a JSON array, g
- remotive: unexpected API response — expected { jobs: [...] }
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/f8bebe6a6a1ce381.
Report an issue: GitHub.