santifer/career-ops · error
eightfold: URL must use HTTPS
Error message
eightfold: URL must use HTTPS: ${url} What it means
This is the second check in assertEightfoldUrl(), the Eightfold provider's SSRF guard: after the URL parses successfully, its protocol must be exactly 'https:'. Any http:, ftp:, or other scheme throws. HTTPS is required because the provider calls a zero-auth JSON API and must not send requests (or leak paths/tenants) over plaintext.
Solutions
- Change the scheme to https:// in the entry's api:/careers_url value — Eightfold tenants are all served over HTTPS.
- If you were pointing at a local mock/test endpoint, use an https test harness or inject a fetch stub rather than an http URL, since the guard rejects it by design.
- Confirm after fixing that the hostname still matches <tenant>.eightfold.ai, otherwise the next guard (untrusted hostname) throws instead.
- Run `new URL(value).protocol` in Node to verify the scheme before saving the config.
Example fix
// before (portals.yml) - name: bayer api: http://bayer.eightfold.ai/careers // after - name: bayer api: https://bayer.eightfold.ai/careers
Defensive patterns
Strategy: validation
Validate before calling
const u = new URL(entry.api || entry.careers_url);
if (u.protocol !== 'https:') throw new Error(`entry ${entry.name}: use https:// — provider rejects ${u.protocol}// URLs`); Type guard
function isHttpsUrl(v) {
if (typeof v !== 'string') return false;
try { return new URL(v).protocol === 'https:'; } catch { return false; }
} Try / catch
try {
await provider.fetch(entry, ctx);
} catch (e) {
if (e.message.startsWith('eightfold: URL must use HTTPS:')) {
// deterministic config issue — rewrite http: to https: and continue; no retry loop
const fixed = e.message.split(': ').pop().replace(/^http:/, 'https:');
console.error(`Rewrite entry to ${fixed}`);
} else throw e;
} Prevention
- Standardize on https:// for every careers_url/api value in portals.yml.
- Add a config lint that rejects non-https portal URLs.
- Remember Eightfold tenants are all HTTPS-hosted; http mirrors or proxies are never valid inputs.
- After fixing the scheme, also confirm the host matches <tenant>.eightfold.ai to pass the next guard.
When it happens
Trigger: assertEightfoldUrl receives a syntactically valid URL whose parsed.protocol is not 'https:' — typically an http:// link such as http://bayer.eightfold.ai/careers, or a protocol-relative/custom scheme that the URL constructor still parses.
Common situations: A portals.yml entry copy-pasted from a browser's insecure-rewrite of the URL; an internal proxy or mirror URL written with http://; a config generator emitting http by default; someone pointing the entry at a local/testing endpoint.
Understand the failure class
Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.
Related errors
- arbeitnow: URL must use HTTPS
- manfred: URL must use HTTPS
- 4dayweek: URL must use HTTPS
- ashby: URL must use HTTPS
- bamboohr: URL must use HTTPS
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/423b84803f4f258e.
Report an issue: GitHub.
Appendix: source
Thrown at providers/eightfold.mjs:71
// Eightfold's edge rate-limits bursts, and a 616-job board is 62 requests.
const INTER_PAGE_DELAY_MS = 250;
const RETRY_POLICY = { retries: 3, baseDelayMs: 500, maxDelayMs: 8_000 };
/**
* SSRF guard — every request URL passes through here before it is fetched.
*
* @param {string} url
* @returns {string} the same URL, when it is a trusted Eightfold endpoint.
*/
function assertEightfoldUrl(url) {
let parsed;
try {
parsed = new URL(url);
} catch {
throw new Error(`eightfold: invalid URL: ${url}`);
}
if (parsed.protocol !== 'https:') throw new Error(`eightfold: URL must use HTTPS: ${url}`);
if (!EIGHTFOLD_HOST_RE.test(parsed.hostname)) {
throw new Error(`eightfold: untrusted hostname "${parsed.hostname}" — must match *.eightfold.ai`);
}
return url;
}
/** @param {number} ms @param {any} ctx */
function sleep(ms, ctx) {
if (typeof ctx?.sleep === 'function') return ctx.sleep(ms);
return new Promise((resolve) => setTimeout(resolve, ms));
}
/**
* Eightfold reports timestamps as epoch SECONDS (`t_create`, `t_update`), not
* the ISO strings every other provider gets. Converted here; anything
* non-finite or non-positive is dropped rather than guessed at.
*
* @param {unknown} valueView on GitHub (pinned to aac998c7ed)