santifer/career-ops · error
eightfold: cannot derive API URL for
Error message
eightfold: cannot derive API URL for ${entry.name} What it means
provider.fetch() starts by resolving the tenant (host + optional domain) from the portal entry via resolveTenant(), which accepts only entry.api or entry.careers_url values that parse as HTTPS URLs on a *.eightfold.ai host. If neither field qualifies, fetch throws this error naming the entry — it cannot construct the /api/apply/v2/jobs endpoint, so it refuses to guess.
Solutions
- Set careers_url to https://<tenant>.eightfold.ai/careers (e.g. https://bayer.eightfold.ai/careers), or set entry.api directly to the eightfold.ai host — api takes precedence over careers_url.
- Check the URL parses and uses https:// — a bare hostname like bayer.eightfold.ai is skipped silently by resolveTenant and surfaces as this error.
- Confirm the hostname is a single-label subdomain of eightfold.ai (matches /^[a-z0-9-]+\.eightfold\.ai$/i); branded CNAMEs like careers.company.com are rejected by design.
- If the entry's branded careers page must stay, keep it in careers_url and add an explicit api: https://<tenant>.eightfold.ai field to pin the tenant.
Example fix
// before - name: Bayer careers_url: https://careers.bayer.com // after - name: Bayer careers_url: https://careers.bayer.com # display only api: https://bayer.eightfold.ai/careers # tenant host pinned via api
Defensive patterns
Strategy: validation
Validate before calling
function canResolveEightfoldTenant(entry) {
for (const raw of [entry?.api, entry?.careers_url]) {
if (typeof raw !== 'string' || !raw) continue;
try {
const u = new URL(raw);
if (u.protocol === 'https:' && /^[a-z0-9-]+\.eightfold\.ai$/i.test(u.hostname)) return true;
} catch { /* skip */ }
}
return false;
} Type guard
const hasEightfoldTenant = (entry) => canResolveEightfoldTenant(entry);
Try / catch
if (!canResolveEightfoldTenant(entry)) {
console.warn(`Skipping ${entry.name}: no https://*.eightfold.ai api/careers_url`);
} else {
const jobs = await provider.fetch(entry, ctx);
} Prevention
- Require https:// scheme in careers_url — bare hostnames are silently skipped by resolveTenant
- Use entry.api to pin the eightfold.ai host when the careers page is branded
- Run provider.detect(entry) first; a null result predicts this fetch error
- Keep the tenant regex at hand when hand-editing portals.yml
When it happens
Trigger: fetch(entry) with entry.api missing/unset and entry.careers_url either absent, not a string, not parseable by new URL(), not https:, or on a host that fails EIGHTFOLD_HOST_RE (e.g. a branded CNAME like careers.company.com, an http: URL, or a malformed URL like 'bayer.eightfold.ai' with no scheme).
Common situations: A portals.yml entry points at the company's branded careers site instead of the canonical eightfold.ai tenant host; the careers_url lost its https:// scheme during hand-editing; the entry uses a custom field name (e.g. url: instead of careers_url:) that resolveTenant never reads; detect() returned null for this entry but an explicit provider selection forced fetch anyway.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- a16z-speedrun-talent: invalid URL
- agentic-jobs: invalid URL
- 4dayweek: untrusted hostname
- a16z-speedrun-talent: untrusted hostname
- a16z-speedrun-talent: URL must use HTTPS
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/6056ba645eb62c41.
Report an issue: GitHub.
Appendix: source
Thrown at providers/eightfold.mjs:277
return Number.isFinite(hint) && hint > 0 ? Math.min(fromEntry, Math.floor(hint)) : fromEntry;
}
/** @type {Provider} */
export default {
id: 'eightfold',
detect(entry) {
try {
const tenant = resolveTenant(entry);
return tenant ? { url: buildApiUrl(tenant, 0, PAGE_SIZE) } : null;
} catch {
return null;
}
},
async fetch(entry, ctx) {
const tenant = resolveTenant(entry);
if (!tenant) throw new Error(`eightfold: cannot derive API URL for ${entry.name}`);
const maxPages = resolveMaxPages(entry, ctx);
const all = [];
/** @type {number|null} */
let total = null;
for (let page = 0; page < maxPages; page++) {
const start = page * PAGE_SIZE;
const apiUrl = buildApiUrl(tenant, start, PAGE_SIZE);
assertEightfoldUrl(apiUrl); // SSRF guard before every fetch
if (page > 0) await sleep(INTER_PAGE_DELAY_MS, ctx);
const json = /** @type {any} */ (await fetchJsonWithRetry(
/** @type {any} */ (ctx),
apiUrl,
{
// redirect:'error' prevents SSRF via a server-side redirect; with
// assertEightfoldUrl above it guarantees the final hostname staysView on GitHub (pinned to aac998c7ed)