santifer/career-ops · error
flowxtra: unexpected API response on page
Error message
flowxtra: unexpected API response on page ${page} — expected { data: { data: [...] } }, got keys: [${json ? Object.keys(json).join(', ') : 'null'}] What it means
In fetch (providers/flowxtra.mjs), each page of the Flowxtra API is fetched via ctx.fetchJson with redirect:'error' (SSRF guard) and asserted to follow the expected envelope { data: { data: [...] } }. This throw fires when the JSON response's data.data is not an array, and the message includes the actual top-level keys (or null) for diagnosis. It means the API answered but not with the documented paginated-jobs shape.
Solutions
- Read the 'got keys' in the message to identify what was actually returned (e.g. message/errors → API error; entirely different keys → envelope change).
- Verify API credentials/status with the Flowxtra admin — an auth or quota error often changes the response shape.
- Check whether the Flowxtra API version changed and update the parsing path in normalizeFlowxtraJob's caller accordingly.
- Confirm the request URL (status=Live&per_page&page) is still a supported query; adjust constants if the API contract moved.
Example fix
// before
const rows = json?.data?.data;
// after (tolerate a flattened envelope)
const rows = Array.isArray(json?.data?.data) ? json.data.data
: Array.isArray(json?.data) ? json.data : null;
if (!rows) throw new Error(`flowxtra: unexpected API response on page ${page}: ${JSON.stringify(json).slice(0, 200)}`); Defensive patterns
Strategy: type-guard
Validate before calling
function looksLikeFlowxtraEnvelope(json) {
return !!json && typeof json === 'object' && Array.isArray(json?.data?.data);
} Type guard
const isJobEnvelope = (json) => typeof json === 'object' && json !== null && Array.isArray(json?.data?.data);
Try / catch
try {
const json = await ctx.fetchJson(url, { redirect: 'error' });
if (!isJobEnvelope(json)) {
console.error('Flowxtra envelope changed; keys:', Object.keys(json ?? {}).join(','));
// inspect json.message / json.errors before retrying
}
} catch (e) {
if (e.message.includes('unexpected API response')) { /* fall back or alert */ }
else throw e;
} Prevention
- Pin/monitor the Flowxtra API version you integrate against.
- Alert on any response whose top-level keys are not [data] — it usually means auth or quota errors.
- Test the provider against a recorded fixture of the real envelope in CI.
When it happens
Trigger: The endpoint returns an error payload ({message, errors}), an auth/limit envelope, an empty object, an HTML error page that somehow parsed as JSON, or a version change renaming the data field — any case where json?.data?.data is not an Array.
Common situations: Flowxtra API version bump changing the pagination envelope; API key expiring so the server returns an error JSON; per_page/page parameters rejected and an error object returned; network middleware (WAF) returning JSON-formatted block responses.
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
- 4dayweek: unexpected API response on page
- a16z-speedrun-talent: unexpected API response on page
- garena: unexpected API response — expected jobs[], got keys
- agentic-jobs: unexpected API response shape on page
- arbeitnow: unexpected API response on page
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/6f691a0c8f5b13b2.
Report an issue: GitHub.
Appendix: source
Thrown at providers/flowxtra.mjs:130
}
/** @type {Provider} */
export default {
id: 'flowxtra',
async fetch(entry, ctx) {
const maxPages = resolveMaxPages(entry);
const fallbackCompany = entry?.name;
const out = [];
for (let page = 1; page <= maxPages; page++) {
const url = `${JOBS_ENDPOINT}?status=Live&per_page=${PER_PAGE}&page=${page}`;
assertFlowxtraEndpointUrl(url);
// redirect:'error' prevents SSRF via server-side redirects
const json = /** @type {any} */ (await ctx.fetchJson(url, { redirect: 'error' }));
const rows = json?.data?.data;
if (!Array.isArray(rows)) {
throw new Error(
`flowxtra: unexpected API response on page ${page} — expected { data: { data: [...] } }, got keys: [${json ? Object.keys(json).join(', ') : 'null'}]`,
);
}
for (const j of rows) {
const normalized = normalizeFlowxtraJob(j, fallbackCompany);
if (normalized) out.push(normalized);
}
if (!json.data.next_page_url || rows.length < PER_PAGE) break; // last page reached
}
return out;
},
};
View on GitHub (pinned to aac998c7ed)