santifer/career-ops · error · Error
a16z-speedrun-talent: unexpected API response on page ${page
Error message
a16z-speedrun-talent: unexpected API response on page ${page} — expected { jobs: [...] }, got keys: [${json ? Object.keys(json).join(', ') : 'null'}] What it means
Each page of the a16z-speedrun-talent feed must be a JSON object with a `jobs` array. After the (retried) fetch, the provider checks `json.jobs` is an array; if json is null or json.jobs is not an array, it throws, naming the page and listing the actual keys received. This is a loud-failure contract guard against a silent truncated board (a blip mid-sweep previously returned nothing for the whole provider — see #2506 — so transient errors are retried first, then this guard catches a genuine shape change).
Source
Thrown at providers/a16z-speedrun-talent.mjs:170
assertFeedUrl(FEED_BASE);
const maxPages = resolveMaxPages(entry);
const q = resolveQuery(entry);
const fallbackCompany = entry?.name;
const out = [];
for (let page = 0; page < maxPages; page++) {
const params = new URLSearchParams({ page: String(page), source: 'career-ops' });
if (q) params.set('q', q);
const url = `${FEED_BASE}?${params}`;
// redirect:'error' prevents SSRF via server-side redirects.
// Retried on transient upstream failures (429/5xx/timeout): this board
// paginates into the hundreds of pages, so a single blip mid-sweep used
// to abort the whole provider and return NOTHING. Retries are bounded
// and, once exhausted, the error still propagates — a silent partial
// board would be worse than an loud empty one (#2506).
const json = await fetchJsonWithRetry(ctx, url, { redirect: 'error' });
if (!json || !Array.isArray(json.jobs)) {
throw new Error(
`a16z-speedrun-talent: unexpected API response on page ${page} — expected { jobs: [...] }, got keys: [${json ? Object.keys(json).join(', ') : 'null'}]`,
);
}
for (const j of json.jobs) {
const normalized = normalizeSpeedrunJob(j, fallbackCompany);
if (normalized) out.push(normalized);
}
// Stop at the last page: a short page, or past the reported total_pages.
if (json.jobs.length < PER_PAGE) break;
if (Number.isInteger(json.total_pages) && page + 1 >= json.total_pages) break;
// Cap warning (same pattern as jibeapply/workday): the feed had more
// pages than we were allowed to read — surface it, with the fix.
if (page + 1 >= maxPages && Number.isInteger(json.total_pages) && json.total_pages > maxPages) {
const name = typeof fallbackCompany === 'string' && fallbackCompany ? fallbackCompany : 'a16z speedrun talent network';
console.error(
`⚠️ a16z-speedrun-talent: ${name} truncated at max_pages=${maxPages} (${out.length} of ${Number.isInteger(json.total) ? json.total : 'many'} jobs) — raise max_pages on this entry or narrow with q: for more`,
);
}View on GitHub (pinned to 9b17a8ac97)
Solutions
- Read the 'got keys' list in the error to identify the actual shape (e.g. [error, message] = error envelope; [data] = renamed field).
- If upstream renamed the field, update the check and normalizeSpeedrunJob in providers/a16z-speedrun-talent.mjs to the new key.
- If it is an error envelope, check a16z/speedrun API status; retry later if transient.
- If fetchJson returned null, investigate the network/status layer.
Example fix
// If upstream renamed `jobs` → `results`:
// before
if (!json || !Array.isArray(json.jobs)) { throw ... }
for (const j of json.jobs) { ... }
// after
if (!json || !Array.isArray(json.results)) { throw ... }
for (const j of json.results) { ... } Defensive patterns
Strategy: try-catch
Validate before calling
// Probe a single page before a full sweep to fail early with a clear message.
function isValidJobsPage(json) {
return !!json && Array.isArray(json.jobs);
}
const probe = await ctx.fetchJson(`${FEED_BASE}?page=0&source=career-ops`);
if (!isValidJobsPage(probe)) throw new Error('a16z feed shape unexpected; not sweeping.'); Type guard
/** @param {unknown} j @returns {j is { jobs: unknown[], total_pages?: number, has_more?: boolean }} */
function isJobsPage(j) {
return !!j && typeof j === 'object' && Array.isArray((/** @type {any} */ (j)).jobs);
} Try / catch
try {
await provider.fetch(entry, ctx);
} catch (err) {
if (/unexpected API response/.test(String(err?.message))) {
console.error('a16z feed shape changed — inspect received keys, update parser.');
}
throw err;
} Prevention
- Pin a feed fixture in CI to catch upstream renames.
- This provider already retries transient (429/5xx/timeout) failures before this guard — respect that and only act on genuine shape changes.
- Log the received keys (the error does) to make diagnosis fetch-free.
When it happens
Trigger: The a16z API returns a non-JSON body (null), an error envelope without jobs, renames `jobs` in a new API version, or returns an HTML/JSON error page. Because the fetch is retried on 429/5xx/timeout, this fires only when the body is structurally wrong, not on a transient blip.
Common situations: Upstream API version bump renaming the jobs field; maintenance/error JSON envelope; CDN/WAF returning a wrapped response; the board's API moved and FEED_BASE points at a stale endpoint returning a different shape.
Related errors
- agentic-jobs: unexpected API response shape on page ${page}
- agentic-jobs: parsed 0 jobs from the API — the response shap
- a16z-speedrun-talent: invalid URL: ${url}
- USAGE
- Apify did not return a run id: ${JSON.stringify(body).slice(
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/916d448f0d28a56b.
Report an issue: GitHub.