santifer/career-ops · error · Error

getonbrd: unexpected API response on page ${page} — expected

Error message

getonbrd: unexpected API response on page ${page} — expected { data: [...] }, got keys: [${json ? Object.keys(json).join(', ') : 'null'}]

What it means

getonbrd.mjs throws this in fetch() when the JSON body for a page is null/falsy or json.data is not an Array. The Get on Board JSON:API contract is { data: [...resources], meta: {...} }, so a missing or reshaped data array means the response is not a valid job feed page. The message includes the actual top-level keys (or 'null') to aid diagnosis.

Source

Thrown at providers/getonbrd.mjs:122

  return job;
}

/** @type {Provider} */
export default {
  id: 'getonbrd',

  async fetch(entry, ctx) {
    assertGetonbrdUrl(FEED_BASE);
    const maxPages = resolveMaxPages(entry);
    const fallbackCompany = entry?.name;
    const out = [];

    for (let page = 1; page <= maxPages; page++) {
      const url = `${FEED_BASE}?per_page=${PER_PAGE}&expand[]=company&page=${page}`;
      // redirect:'error' prevents SSRF via server-side redirects
      const json = await ctx.fetchJson(url, { redirect: 'error' });
      if (!json || !Array.isArray(json.data)) {
        throw new Error(
          `getonbrd: unexpected API response on page ${page} — expected { data: [...] }, got keys: [${json ? Object.keys(json).join(', ') : 'null'}]`,
        );
      }
      for (const j of json.data) {
        const normalized = normalizeGetonbrdJob(j, fallbackCompany);
        if (normalized) out.push(normalized);
      }
      if (json.data.length < PER_PAGE) break; // short page → last page reached
    }
    return out;
  },
};

View on GitHub (pinned to 9b17a8ac97)

Solutions

  1. curl the failing page URL (https://www.getonbrd.com/api/v0/categories/programming/jobs?per_page=100&expand[]=company&page=N) and inspect the keys named in the message.
  2. If it is an error/maintenance envelope, retry shortly — usually transient.
  3. If the shape changed permanently, update the json.data extraction and the short-page break logic in getonbrd.mjs.
  4. Confirm expand[]=company and per_page=100 still match the documented API; an API tightening may reject the current query string.

Example fix

// before
if (!json || !Array.isArray(json.data)) { throw new Error(`getonbrd: unexpected API response ...`); }

// after (tolerate a renamed key, e.g. 'results')
const rows = Array.isArray(json?.data) ? json.data : Array.isArray(json?.results) ? json.results : null;
if (!rows) { throw new Error(`getonbrd: unexpected API response ...`); }
Defensive patterns

Strategy: try-catch

Type guard

// Narrow a Get on Board response to the expected { data: [] } shape.
function isGetonbrdPage(json) {
  return !!json && typeof json === 'object' && Array.isArray(json.data);
}

Try / catch

// On non-first pages, treat a shape anomaly as end-of-feed instead of fatal.
try {
  const json = await ctx.fetchJson(url, { redirect: 'error' });
  if (!isGetonbrdPage(json)) {
    if (page === 1) throw new Error('getonbrd: unexpected API response on page 1');
    break;
  }
} catch (err) {
  if (page === 1) throw err;
  console.error(`getonbrd: page ${page} failed — ${err.message}`);
  break;
}

Prevention

When it happens

Trigger: The API returned an error envelope ({ message, errors }) with no data; a maintenance/HTML page was parsed into a non-standard object; the endpoint was versioned and moved jobs under a different key; a rate-limit response returned JSON without data; the body was empty (null json).

Common situations: Get on Board ships an API version bump that reshapes the envelope; the board is temporarily unavailable and returns an error object; a WAF/Cloudflare challenge returns interstitial JSON; invalid query params (e.g. an unknown expand) triggered an error response.

Related errors


AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13). Data as JSON: /api/errors/695c7bfd620a9fc4. Report an issue: GitHub.