santifer/career-ops · error · Error
lever: invalid URL: ${url}
Error message
lever: invalid URL: ${url} What it means
assertLeverUrl mirrors the Larajobs pattern: it first attempts new URL(url). If the constructor throws — the string is not an absolute parseable URL — this error fires. It guards the Lever postings fetch and the api: auto-detection path.
Source
Thrown at providers/lever.mjs:16
// @ts-check
/** @typedef {import('./_types.js').Provider} Provider */
// 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']);
/** @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 9b17a8ac97)
Solutions
- Read the value in the message — it is the exact unparseable string.
- Set api: to a full URL such as https://api.lever.co/v0/postings/<slug>.
- Strip whitespace/newlines from the config value.
- If relying on auto-detection from careers_url, ensure careers_url is a full https URL containing lever.co.
Example fix
// before (portals.yml) api: lever.co/postings/acme // after api: https://api.lever.co/v0/postings/acme
Defensive patterns
Strategy: validation
Validate before calling
import { URL } from 'node:url';
export function isValidAbsoluteUrl(value) {
if (typeof value !== 'string' || !value) return false;
try { new URL(value); return true; } catch { return false; }
}
// Pre-validate before the provider runs:
// if (!isValidAbsoluteUrl(entry.api)) failConfig(`lever api invalid for ${entry.name}`); Type guard
/** @param {string} url */
function isParseableUrl(url) {
try { new URL(url); return true; } catch { return false; }
} Try / catch
try {
assertLeverUrl(entry.api);
} catch (err) {
console.warn(`lever entry ${entry.name} has bad api URL: ${err.message}`);
entry.disabled = true;
} Prevention
- Pre-flight every lever entry's api: field in the config loader.
- Always specify the full https://api.lever.co/... URL rather than relying on partial strings.
- For auto-detection, ensure careers_url is a full https URL containing lever.co.
When it happens
Trigger: entry.api or a careers_url-derived value passed to assertLeverUrl is empty, relative, missing a scheme, or otherwise rejected by the URL constructor. resolveApiUrl calls assertLeverUrl on entry.api when present, and on the derived api.lever.co URL.
Common situations: portals.yml api: field is blank or missing https://; a slug-based derivation produced an incomplete URL; whitespace/control characters were pasted into the config.
Related errors
- flowxtra: untrusted hostname "${parsed.hostname}" — must be
- gem: invalid URL: ${url}
- gem: URL must use HTTPS: ${url}
- gem: untrusted hostname "${parsed.hostname}" — must be one o
- getonbrd: invalid URL: ${url}
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/d2ae3fe030232a12.
Report an issue: GitHub.