santifer/career-ops · error · Error
greenhouse: URL must use HTTPS: ${url}
Error message
greenhouse: URL must use HTTPS: ${url} What it means
greenhouse.mjs throws this in assertGreenhouseUrl() after the URL parses but its protocol is not 'https:'. It enforces TLS for the Greenhouse boards API. Because the guard covers the operator-supplied entry.api (and the derived careers_url-built URL), a live throw usually comes from an http:// api: value in portals.yml.
Source
Thrown at providers/greenhouse.mjs:22
// Greenhouse provider — hits the public boards-api JSON endpoint.
// Handles both explicit `api:` URLs and auto-detection from `careers_url`.
const ALLOWED_GREENHOUSE_HOSTS = new Set([
'boards-api.greenhouse.io',
'boards.greenhouse.io',
'job-boards.greenhouse.io',
'job-boards.eu.greenhouse.io',
]);
/** @param {string} url */
function assertGreenhouseUrl(url) {
let parsed;
try {
parsed = new URL(url);
} catch {
throw new Error(`greenhouse: invalid URL: ${url}`);
}
if (parsed.protocol !== 'https:') throw new Error(`greenhouse: URL must use HTTPS: ${url}`);
if (!ALLOWED_GREENHOUSE_HOSTS.has(parsed.hostname))
throw new Error(`greenhouse: untrusted hostname "${parsed.hostname}" — must be one of: ${[...ALLOWED_GREENHOUSE_HOSTS].join(', ')}`);
return url;
}
/** @param {import('./_types.js').PortalEntry} entry */
function resolveApiUrl(entry) {
if (entry.api) {
assertGreenhouseUrl(entry.api);
return entry.api;
}
const url = entry.careers_url || '';
const match = url.match(/job-boards(?:\.eu)?\.greenhouse\.io\/([^/?#]+)/);
if (match) return `https://boards-api.greenhouse.io/v1/boards/${match[1]}/jobs`;
return null;
}
// NaN-safe Date.parse — `|| undefined` would also coerce a valid epoch 0.View on GitHub (pinned to 9b17a8ac97)
Solutions
- Set api to its https:// form or remove api to let the provider derive an HTTPS URL from careers_url.
- For local interception, trust a proxy CA against the HTTPS endpoint instead of downgrading.
- Remove tests that pass http URLs unless they assert this specific throw.
Example fix
# before - name: Acme provider: greenhouse api: http://boards-api.greenhouse.io/v1/boards/acme/jobs # after - name: Acme provider: greenhouse api: https://boards-api.greenhouse.io/v1/boards/acme/jobs
Defensive patterns
Strategy: validation
Validate before calling
// Reject non-HTTPS Greenhouse api values before they reach the provider.
function greenhouseApiIsHttps(entry) {
if (!entry.api) return true; // derived URL is always https
try { return new URL(entry.api).protocol === 'https:'; } catch { return false; }
} Prevention
- Never set api to http://; use a trusted proxy CA against the HTTPS endpoint for local interception.
- Add a lint rule forbidding http: in any provider api: field.
- Prefer careers_url derivation (which always yields https) over a hand-written api.
When it happens
Trigger: entry.api is set to an http:// URL (e.g. for local proxying); a careers_url used http:// and was the basis for a derived http URL in a stale code path; a test calls assertGreenhouseUrl('http://boards-api.greenhouse.io/...').
Common situations: Local debugging through a non-TLS intercepting proxy; a copy-paste of an old http Greenhouse link; an env override downgraded the scheme.
Related errors
- glints: URL must use HTTPS: ${url}
- greenhouse: invalid URL: ${url}
- gem: URL must use HTTPS: ${url}
- getonbrd: URL must use HTTPS: ${url}
- glints: invalid URL: ${url}
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/dd769a534f54bd46.
Report an issue: GitHub.