santifer/career-ops · error · Error
recruitee: invalid URL: ${url}
Error message
recruitee: invalid URL: ${url} What it means
assertRecruiteeUrl throws this when new URL(url) itself raises — i.e. the string is not a parseable absolute URL at all (no scheme, invalid characters, unbalanced brackets). This is the first guard in the chain, before the HTTPS and hostname checks. It means the URL is structurally broken, not merely wrong-hosted.
Source
Thrown at providers/recruitee.mjs:17
// @ts-check
/** @typedef {import('./_types.js').Provider} Provider */
// Recruitee provider — hits the public per-tenant offers API.
// Auto-detects from careers_url pattern `https://<slug>.recruitee.com`.
// Per-tenant subdomains are the variable part — SSRF defence uses a
// regex match on `<safe-slug>.recruitee.com` rather than a static
// allowlist.
const RECRUITEE_HOST_RE = /^[a-z0-9][a-z0-9-]*\.recruitee\.com$/;
function assertRecruiteeUrl(url) {
let parsed;
try {
parsed = new URL(url);
} catch {
throw new Error(`recruitee: invalid URL: ${url}`);
}
if (parsed.protocol !== 'https:') throw new Error(`recruitee: URL must use HTTPS: ${url}`);
if (!RECRUITEE_HOST_RE.test(parsed.hostname)) {
throw new Error(`recruitee: untrusted hostname "${parsed.hostname}" — must match <slug>.recruitee.com`);
}
return url;
}
function resolveApiUrl(entry) {
const raw = typeof entry.careers_url === 'string' ? entry.careers_url : '';
if (!raw) return null;
let parsed;
try {
parsed = new URL(raw);
} catch {
return null;
}
if (parsed.protocol !== 'https:') return null;View on GitHub (pinned to 9b17a8ac97)
Solutions
- Prepend 'https://' if the hostname is present but the scheme is missing.
- Validate the URL string with new URL() or a URL-pattern check before passing it into the provider.
- Check for stray characters, leading/trailing whitespace, or smart quotes in the source config.
- Ensure the value is actually a string and not null/undefined/number.
Example fix
// before
const url = entry.careers_url; // 'acme.recruitee.com'
assertRecruiteeUrl(url);
// after
const url = entry.careers_url.startsWith('http') ? entry.careers_url : `https://${entry.careers_url}`; Defensive patterns
Strategy: validation
Validate before calling
function ensureAbsoluteHttpsUrl(raw) {
if (typeof raw !== 'string' || !raw.trim()) return null;
let s = raw.trim();
if (!/^https?:\/\//i.test(s)) s = 'https://' + s;
try { new URL(s); return s; } catch { return null; }
}
const safe = ensureAbsoluteHttpsUrl(entry.careers_url);
if (!safe) { console.warn(`skip ${entry.name}: unparseable URL`); } Type guard
null
Try / catch
try {
await provider.fetch(entry, ctx);
} catch (e) {
if (/recruitee: invalid URL/.test(e.message)) {
console.warn(`[skip] ${entry.name}: structurally invalid URL — ${entry.careers_url}`);
} else throw e;
} Prevention
- Store URLs always with an explicit https:// scheme.
- Trim whitespace from user-supplied URLs before persistence.
- Run new URL() at config-load to catch malformed entries early.
When it happens
Trigger: A URL string like 'acme.recruitee.com' (no protocol), 'https://' (bare scheme), 'ht!tps://x.recruitee.com', or a non-string accidentally coerced. Any value that makes the URL constructor throw.
Common situations: The careers_url was stored without a protocol; a template/format string produced an empty or partial URL; user input was not trimmed or validated before reaching the provider.
Related errors
- recruitee: URL must use HTTPS: ${url}
- recruitee: untrusted hostname "${parsed.hostname}" — must ma
- remotli: invalid URL: ${url}
- rippling: invalid URL: ${url}
- smartrecruiters: invalid URL: ${url}
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/31072749a861b73b.
Report an issue: GitHub.