santifer/career-ops · error · Error
themuse: unexpected API response on page 0 — expected { resu
Error message
themuse: unexpected API response on page 0 — expected { results: [...] }, got keys: [${first ? Object.keys(first).join(', ') : 'null'}] What it means
The Muse provider fetches page 0 of the feed and requires the JSON body to contain a 'results' array. If the first page is missing, not an object, or lacks a results array, fetch() throws this error naming the keys actually received. It is a fail-fast shape check before any pagination starts.
Source
Thrown at providers/themuse.mjs:147
export default {
id: 'themuse',
async fetch(_entry, ctx) {
assertMuseUrl(FEED_BASE);
// Page 0 is fetched outside the tolerant loop below and its failure is
// NOT caught: a completely dead board must throw, not return []. A
// caught page-0 failure would return an empty array indistinguishable
// from a healthy "0 jobs today" result -- scan.mjs's consecutive-failure
// detector resets its streak on any non-throwing fetch, so a themuse
// outage would silently reset the very detector meant to catch it.
// Mirrors workday.mjs, which fetches its first page outside the
// retry-tolerant loop (`page = 1` start) for the same reason.
const firstUrl = `${FEED_BASE}?page=0`;
// redirect:'error' prevents SSRF via server-side redirects
const first = await fetchPageWithRetry(ctx, firstUrl, { redirect: 'error' });
if (!first || !Array.isArray(first.results)) {
throw new Error(
`themuse: unexpected API response on page 0 — expected { results: [...] }, got keys: [${first ? Object.keys(first).join(', ') : 'null'}]`,
);
}
const allResults = [...first.results];
const pageCount = Number.isInteger(first.page_count) && first.page_count > 1
? Math.min(first.page_count, MAX_PAGES)
: 1;
// Pages 1+ stay tolerant: a page that exhausts retries, OR comes back
// with an unexpected shape (a successful fetch, no retry involved --
// caught here so it lands in the same truncation path instead of
// escaping uncaught and discarding allResults), truncates with a warning
// and returns whatever was already gathered instead of discarding it.
for (let page = 1; page < pageCount; page++) {
await sleep(INTER_PAGE_DELAY_MS, ctx);
const url = `${FEED_BASE}?page=${page}`;
let json;
try {View on GitHub (pinned to 1696bec4d0)
Solutions
- Log/inspect the received keys (the message prints them) and compare against the documented Muse response shape.
- Retry later or with backoff — transient 5xx/rate-limit responses often surface as malformed bodies.
- Verify any API credentials/base URL config for the Muse provider are current.
- If the Muse API changed its envelope, update the shape check in providers/themuse.mjs fetch() to the new schema.
- Check network egress — a proxy or WAF may be replacing the body with its own error JSON.
Defensive patterns
Strategy: type-guard
Type guard
function isMusePage(body) {
return !!body && typeof body === 'object' && !Array.isArray(body) &&
Array.isArray(body.results);
} Try / catch
try {
const jobs = await museProvider.fetch(entry, ctx);
} catch (err) {
if (String(err.message).includes('unexpected API response on page 0')) {
// inspect received shape, back off, retry later
console.warn('Muse feed unavailable or envelope changed:', err.message);
} else throw err;
} Prevention
- Monitor the Muse API for envelope changes; pin alerts on provider releases.
- Check provider/API status before long scan runs.
- Keep API credentials/quota valid — error-shaped bodies usually mean auth or rate-limit trouble.
- Fail fast in CI: run a single-page smoke fetch before batch scans.
When it happens
Trigger: GET {FEED_BASE}?page=0 returns JSON that is null, an array, or an object without a 'results' array — e.g. an API error payload ({error: ...}), an HTML error page parsed as text, a rate-limit response, or a schema change on the Muse API.
Common situations: Muse API outage or maintenance returning error JSON; API key/quota exhaustion producing an error-shaped body; The Muse changing its response envelope in a new API version; a proxy/captive portal returning its own JSON error page.
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
- torre: unexpected API response — expected { results: [...] }
- HTTP ${res.status}${snippet ? ': ' + snippet.replace(/\s+/g,
- vc-portfolios: YC API fetch failed — ${err.message}
- [models] Failed to fetch free model list: ${reason}. Check t
- HTTP ${resp.status}: ${t.slice(0, 120)}
AI-assisted analysis of santifer/career-ops@1696bec4d0 (2026-09-01).
Data as JSON: /api/errors/67eff14619d53185.
Report an issue: GitHub.