santifer/career-ops · error · Error

smartrecruiters: invalid URL: ${url}

Error message

smartrecruiters: invalid URL: ${url}

What it means

assertSmartRecruitersUrl throws this when new URL(url) raises — the string is not a parseable absolute URL. This is the structural guard preceding the HTTPS and hostname-allowlist checks. smartrecruiters builds API URLs from a resolved slug and the fixed api.smartrecruiters.com host, so a raw parse failure typically indicates a malformed constructed URL or a bad external input.

Source

Thrown at providers/smartrecruiters.mjs:20

/** @typedef {import('./_types.js').Provider} Provider */

// SmartRecruiters provider — hits the public postings API.
// Auto-detects from careers_url pattern
// `https://(careers|jobs).smartrecruiters.com/<slug>`. A tracked_companies
// entry can also set `provider: smartrecruiters` explicitly to bypass
// detection (useful when the public careers URL is a branded custom domain).

const ALLOWED_SMARTRECRUITERS_HOSTS = new Set(['api.smartrecruiters.com']);
const SR_CAREERS_HOSTS = new Set(['careers.smartrecruiters.com', 'jobs.smartrecruiters.com']);
const SR_PAGE_SIZE = 100;
const SR_MAX_PAGES = 50;  // safety cap (5000 postings @ 100/page)

function assertSmartRecruitersUrl(url) {
  let parsed;
  try {
    parsed = new URL(url);
  } catch {
    throw new Error(`smartrecruiters: invalid URL: ${url}`);
  }
  if (parsed.protocol !== 'https:') throw new Error(`smartrecruiters: URL must use HTTPS: ${url}`);
  if (!ALLOWED_SMARTRECRUITERS_HOSTS.has(parsed.hostname)) {
    throw new Error(`smartrecruiters: untrusted hostname "${parsed.hostname}" — must be one of: ${[...ALLOWED_SMARTRECRUITERS_HOSTS].join(', ')}`);
  }
  return url;
}

function resolveSlug(entry) {
  // entry.api takes precedence over careers_url (mirrors greenhouse/ashby) so a
  // branded page (e.g. https://jobs.continental.com) can stay as careers_url
  // while the SmartRecruiters slug is pinned via
  // api: https://careers.smartrecruiters.com/<slug> in portals.yml.
  for (const raw of [entry.api, entry.careers_url]) {
    if (typeof raw !== 'string' || !raw) continue;
    let parsed;
    try {
      parsed = new URL(raw);

View on GitHub (pinned to 9b17a8ac97)

Solutions

  1. Log the URL string passed into the guard to identify the malformation.
  2. Verify the slug was resolved to a non-empty value before URL construction.
  3. Ensure the API URL is built from the documented base (https://api.smartrecruiters.com/v1/companies/<slug>/postings).
  4. Strip whitespace and validate with new URL() before invoking the provider.

Example fix

// before
assertSmartRecruitersUrl(slug); // slug is a bare token, not a URL
// after
assertSmartRecruitersUrl(`https://api.smartrecruiters.com/v1/companies/${encodeURIComponent(slug)}/postings`);
Defensive patterns

Strategy: validation

Validate before calling

function isParseableUrl(s) {
  try { new URL(s); return true; } catch { return false; }
}
// smartrecruiters builds API URLs from a slug — ensure the slug is non-empty
if (!slug || typeof slug !== 'string') {
  throw new Error('smartrecruiters: empty slug — cannot build API URL');
}
const apiUrl = `https://api.smartrecruiters.com/v1/companies/${encodeURIComponent(slug)}/postings`;
if (!isParseableUrl(apiUrl)) throw new Error('smartrecruiters: built URL is invalid');

Type guard

null

Try / catch

try {
  await provider.fetch(entry, ctx);
} catch (e) {
  if (/smartrecruiters: invalid URL/.test(e.message)) {
    console.error(`[bug] smartrecruiters URL construction broken — check slug resolution`);
  } else throw e;
}

Prevention

When it happens

Trigger: The URL passed in has no protocol, contains illegal characters, or is an empty/partial string that the URL constructor rejects.

Common situations: The slug resolution produced an empty string, yielding a malformed API URL; an external caller passed a non-URL value; a config field contained whitespace or smart quotes.

Related errors


AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13). Data as JSON: /api/errors/afbb8898bf94e4bc. Report an issue: GitHub.