santifer/career-ops · error · Error
radancy: unexpected fragment response shape at page
Error message
radancy: unexpected fragment response shape at page ${page} (results is not a string) What it means
While paging Radancy's search-jobs fragment endpoint, the provider expects each JSON fragment response to carry `results` as a string (the HTML fragment). A missing, null, or wrong-typed `results` is treated as a malformed response and thrown, because coercing it to [] would end pagination early exactly like a genuine empty page with no error.
Solutions
- Re-run the scan; transient CDN/proxy error bodies are the most common cause
- Check whether Radancy changed the fragment response schema and update the provider parser
- Reduce maxPages/limit the walk to shallow pages to isolate which page fails
- Report/inspect the raw response for that tenant board to see what is actually returned
Example fix
null
Defensive patterns
Strategy: try-catch
Validate before calling
null
Type guard
function isWellFormedFragment(json) {
return json !== null && typeof json === 'object' && typeof json.results === 'string';
} Try / catch
try {
jobs = await radancy.fetch(entry, ctx);
} catch (err) {
if (err.message.includes('unexpected fragment response shape')) {
console.warn(`Radancy fragment shape changed for ${entry.name} — retrying later`);
return [];
}
throw err;
} Prevention
- Re-run failed scans before investigating — CDN/proxy error bodies are often transient
- Keep maxPages modest so mid-walk failures are cheap to retry
- Monitor Radancy board behaviour after ATS migrations; the fragment schema is the fragile part
When it happens
Trigger: Radancy returns a fragment JSON whose results field is absent, null, a non-string (object/array/number), typically on a page past 1 — e.g. an HTML error page parsed as JSON, an ATS-side schema change, or the endpoint returning a different shape for deep pages.
Common situations: Radancy changes or rate-limits the fragment endpoint mid-walk; a tenant's board is misconfigured and page N returns an error payload; network middleware (proxy/CDN) injects a JSON error body.
Related errors
- remoteok: unexpected API response — expected a JSON array…
- remotive: unexpected API response — expected
- 4dayweek: unexpected API response on page
- a16z-speedrun-talent: unexpected API response on page
- Access denied: Egress guard blocked private target IP
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/b5d7fbadfb9229af.
Report an issue: GitHub.
Appendix: source
Thrown at providers/radancy.mjs:398
for (; page <= lastPage && jobs.length < maxJobs; page++) {
await wait(PAGE_DELAY_MS);
let rows;
try {
const json = await fetchJsonWithRetry(ctx, buildFragmentUrl(listUrl, page), {
redirect: 'error',
headers: { accept: 'application/json', 'x-requested-with': 'XMLHttpRequest' },
});
// A STRING `results` — even one that parses to zero rows, which
// is what a genuine last page looks like live (Walgreens' own
// past-the-end page answers hasJobs:false with a non-empty
// shell string that simply contains no job rows) — is the only
// form the documented "no more jobs" signal takes. A missing,
// null, or wrong-typed `results` has never been observed as
// that signal, so it's a malformed response, not an empty
// page: silently coercing it to [] would end the walk early
// exactly like a real empty page does, with no error raised.
if (typeof json?.results !== 'string') {
throw new Error(`radancy: unexpected fragment response shape at page ${page} (results is not a string)`);
}
rows = json.results ? parseResults(json.results, origin) : [];
} catch (err) {
if (probing) throw err; // propagate ProbePageBudgetReached (or any rejection) unwrapped
console.error(
`⚠️ radancy: ${entry.name} truncated at page ${page} of ${lastPage}`
+ ` (${jobs.length} jobs): ${err.message}`,
);
stopReason = 'error';
break; // keep what we have; a mid-walk blip shouldn't discard earlier pages
}
// A clean, structured empty page (`rows.length === 0`, not a
// thrown error) is a legitimate natural stop even when it lands
// well short of `totalResults`/`totalPages` — no different from
// any other tenant undercounting. Observed live on one very
// large tenant landing exactly at offset 10,000 (100 pages of
// 100 — the default Elasticsearch/Solr `max_result_window`);
// unlike `workday.mjs`'s analogous, multi-tenant-confirmedView on GitHub (pinned to aac998c7ed)