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

  1. Log/inspect the received keys (the message prints them) and compare against the documented Muse response shape.
  2. Retry later or with backoff — transient 5xx/rate-limit responses often surface as malformed bodies.
  3. Verify any API credentials/base URL config for the Muse provider are current.
  4. If the Muse API changed its envelope, update the shape check in providers/themuse.mjs fetch() to the new schema.
  5. 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

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


AI-assisted analysis of santifer/career-ops@1696bec4d0 (2026-09-01). Data as JSON: /api/errors/67eff14619d53185. Report an issue: GitHub.