santifer/career-ops · error · Error
workday: cannot derive CXS endpoint for
Error message
workday: cannot derive CXS endpoint for ${entry.name} What it means
The Workday provider posts to a CXS (Candidate Experience Service) endpoint derived from the entry via resolveEndpoint. When the entry's api/careers_url cannot be resolved to a CXS endpoint, fetch throws before making any request.
Solutions
- Provide the entry's api field with the full Workday CXS endpoint URL
- Check the careers_url matches the Workday CXS pattern (…/wday/cxs/<tenant>/<site>/jobs) and fix it
- Confirm the tenant/site names by loading the company's careers page; correct the entry config
Example fix
// before
{ name: 'Acme', careers_url: 'https://acme.wd3.myworkdayjobs.com/careers' }
// after
{ name: 'Acme', api: 'https://acme.wd3.myworkdayjobs.com/wday/cxs/acme/careers/jobs' } Defensive patterns
Strategy: validation
Validate before calling
function hasWorkdayCxs(entry) {
const src = entry.api || entry.careers_url || '';
return /myworkdayjobs\.com\/wday\/cxs\/[\w-]+\/[\w-]+/i.test(src) || /wday\/cxs/.test(src);
} Type guard
const hasWorkdayCxs = (e) => /\/wday\/cxs\/[\w-]+\/[\w-]+/i.test(e.api || e.careers_url || '');
Try / catch
try { jobs = await workday.fetch(entry, ctx); } catch (e) { if (e.message.startsWith('workday: cannot derive CXS endpoint')) { console.warn(`${entry.name}: set entry.api to the full CXS /wday/cxs/<tenant>/<site>/jobs URL`); jobs = []; } else throw e; } Prevention
- Store the explicit CXS api URL (…/wday/cxs/tenant/site/jobs) instead of the careers page URL
- Confirm tenant and site names from the live careers page when onboarding a company
- Re-verify entries when a company migrates ATS versions
When it happens
Trigger: fetch(entry, ctx) with an entry lacking a resolvable Workday endpoint — no api field, a non-Workday careers_url, or a careers_url that does not match Workday CXS URL patterns.
Common situations: A portals.yml company entry where the careers page is Workday Classic (not CXS), a wrong tenant hostname, or a company that migrated off Workday.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
AI-assisted analysis of santifer/career-ops@e7abd431fc (2026-09-16).
Data as JSON: /api/errors/25f313deadfad365.
Report an issue: GitHub.
Appendix: source
Thrown at providers/workday.mjs:565
/**
* Fetch all job postings for a Workday-backed entry, paginating through
* the tenant's CXS API.
*
* Some tenants front their CXS API with Cloudflare bot management (seen
* live: geico) that 500s requests missing ordinary browser headers — the
* default UA/accept-language-less request trips it even over plain HTTPS
* with no other red flags. A real Chrome UA + accept-language + matching
* origin/referer clears it without needing per-tenant config (same fix
* as providers/glints.mjs's firewall).
*
* @param {{ name?: string, api?: string, careers_url?: string, max_pages?: number }} entry
* @param {{ fetchJson: (url: string, opts?: object) => Promise<any>, sinceMs?: number, maxPages?: number, syntheticEntries?: boolean }} ctx
* @returns {Promise<Array<{title: string, url: string, company: string, location: string, postedAt?: number}>>}
*/
async fetch(entry, ctx) {
const ep = resolveEndpoint(entry);
if (!ep) throw new Error(`workday: cannot derive CXS endpoint for ${entry.name}`);
const postOpts = {
method: 'POST',
redirect: 'error',
headers: {
'content-type': 'application/json',
accept: 'application/json',
'user-agent': BROWSER_LIKE_USER_AGENT,
'accept-language': 'en-US,en;q=0.9',
origin: ep.origin,
referer: `${ep.jobBase}/`,
},
};
const makeBody = (offset, appliedFacets) => JSON.stringify({ limit: PAGE_SIZE, offset, searchText: '', appliedFacets });
const sinceMs = typeof ctx?.sinceMs === 'number' ? ctx.sinceMs : null;
const maxPages = resolveMaxPages(entry);
// Honor a context page cap — verify-portals' liveness probe setsView on GitHub (pinned to e7abd431fc)