santifer/career-ops · error · Error
lever: invalid URL
Error message
lever: invalid URL: ${url} What it means
The lever provider validates URLs with assertLeverUrl; anything the URL constructor cannot parse throws this error. Unlike the single-host providers, Lever uses a host allowlist (ALLOWED_LEVER_HOSTS), but malformed URLs are rejected first at the parse stage. The module prefix 'lever:' identifies the validator in logs.
Solutions
- Supply a complete absolute URL, e.g. 'https://api.lever.co/v0/postings/company?mode=json'.
- Check the portals.yml entry for empty/missing fields or unresolved variables.
- Validate with new URL(u) in a try/catch before calling the provider.
- Inspect the interpolated value in the message — it shows exactly what string reached the validator.
Example fix
// before
assertLeverUrl(`https://api.lever.co/v0/postings/${entry.slug}`);
// after (entry.slug undefined -> invalid)
if (!entry?.slug) throw new Error('lever: missing company slug');
assertLeverUrl(`https://api.lever.co/v0/postings/${entry.slug}`); Defensive patterns
Strategy: validation
Validate before calling
function isValidUrl(u) { try { new URL(u); return true; } catch { return false; } }
if (!isValidUrl(url)) throw new Error(`skipping lever entry: invalid URL ${url}`); Type guard
function isParseableUrl(v) { return typeof v === 'string' && (() => { try { new URL(v); return true; } catch { return false; } })(); } Try / catch
try {
provider.fetch(entry, ctx);
} catch (e) {
if (e.message.startsWith('lever: invalid URL')) {
console.error(`Bad lever config/entry: ${e.message}`);
return null;
}
throw e;
} Prevention
- Guard optional fields (slug, api) before interpolating into URLs
- Validate generated URLs with new URL() before use
- Fail soft per entry so one bad entry doesn't abort the scan
- Avoid undefined/empty template placeholders in URL construction
When it happens
Trigger: Calling assertLeverUrl (directly or via the provider) with an unparseable string: empty value, missing scheme, spaces, or an unresolved placeholder such as '${entry.api}'.
Common situations: portals.yml entry where the api/careers URL field is blank or truncated, interpolation of an undefined variable producing 'undefined', or hand-editing that splits the URL across lines.
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
- garena: is not a usable URL segment
- landingjobs: invalid URL
- larajobs: invalid URL
- nodesk: invalid URL
- nofluffjobs: invalid URL
AI-assisted analysis of santifer/career-ops@e7abd431fc (2026-09-16).
Data as JSON: /api/errors/d2ae3fe030232a12.
Report an issue: GitHub.
Appendix: source
Thrown at providers/lever.mjs:22
// Lever provider — hits the public postings endpoint.
// Auto-detects from careers_url via jobs.(eu.)?lever.co/<slug>.
// Handles both explicit `api:` URLs and auto-detection from `careers_url`.
const ALLOWED_LEVER_HOSTS = new Set(['api.lever.co', 'api.eu.lever.co']);
// The v0 postings endpoint returns the whole board in one response, with every
// description inlined, so a large board outgrows _http.mjs's 10s default:
// jobgether is 42.8 MB and aborted at 10s on its own (#4177). Same value and
// reasoning as ASHBY_TIMEOUT_MS, the other one-response board-wide ATS feed.
const LEVER_TIMEOUT_MS = 30_000;
/** @param {string} url */
function assertLeverUrl(url) {
let parsed;
try {
parsed = new URL(url);
} catch {
throw new Error(`lever: invalid URL: ${url}`);
}
if (parsed.protocol !== 'https:') throw new Error(`lever: URL must use HTTPS: ${url}`);
if (!ALLOWED_LEVER_HOSTS.has(parsed.hostname))
throw new Error(`lever: untrusted hostname "${parsed.hostname}" — must be one of: ${[...ALLOWED_LEVER_HOSTS].join(', ')}`);
return url;
}
/** @param {import('./_types.js').PortalEntry} entry */
function resolveApiUrl(entry) {
// Explicit api: wins — lets an entry keep a human-facing corporate
// careers_url (e.g. https://www.coalfire.com/careers) while still pinning
// the Lever postings board (mirrors greenhouse's api: precedence).
if (entry.api) {
assertLeverUrl(entry.api);
return entry.api;
}
let url;
try {View on GitHub (pinned to e7abd431fc)