santifer/career-ops · error · Error

jobvite: invalid URL: ${url}

Error message

jobvite: invalid URL: ${url}

What it means

Thrown by assertJobviteHost when new URL(url) throws — the supplied string is not a parseable absolute URL. In the shipped provider, assertJobviteHost is only ever called on URLs built internally via the URL constructor (buildBoardFetchUrl from an encoded slug, buildFeedUrl via new URL + searchParams.set), which always parse. From the public contract this branch is therefore effectively unreachable; it is an SSRF defense-in-depth guard that would fire only if a constructed URL were somehow malformed (e.g. a slug/eid containing characters that break the constructor — encodeURIComponent and searchParams.set already prevent this).

Source

Thrown at providers/jobvite.mjs:97

// The XML feed inlines every job's FULL HTML description, so it is large and
// slow by construction rather than occasionally: Tyler Technologies returns
// 1.88 MB for 236 jobs in ~11s. That overshoots the shared 10s default in
// _http.mjs by a second, which aborted the whole tenant and reported it as a
// network failure. Sized to absorb a genuinely big tenant on a slow link; the
// board page (a normal HTML document) keeps the default.
const FEED_TIMEOUT_MS = 45_000;

/**
 * Pin a URL to the two known Jobvite hosts over HTTPS.
 * @param {string} url
 */
function assertJobviteHost(url) {
  let parsed;
  try {
    parsed = new URL(url);
  } catch {
    throw new Error(`jobvite: invalid URL: ${url}`);
  }
  if (parsed.protocol !== 'https:')
    throw new Error(`jobvite: URL must use HTTPS: ${url}`);
  if (!ALLOWED_HOSTS.has(parsed.hostname))
    throw new Error(`jobvite: untrusted hostname "${parsed.hostname}" — must be ${BOARD_HOST} or ${FEED_HOST}`);
  return url;
}

// NaN-safe Date.parse → epoch ms.
/** @param {string} value */
function toEpochMs(value) {
  if (!value) return undefined;
  const parsed = Date.parse(value);
  return Number.isNaN(parsed) ? undefined : parsed;
}

/**
 * The vanity slug from a Jobvite careers URL, or null.

View on GitHub (pinned to 9b17a8ac97)

Solutions

  1. Only ever pass URL-constructed strings to assertJobviteHost; pre-validate any caller input with new URL(...) in a try/catch.
  2. Keep BOARD_HOST/FEED_HOST as well-formed hostnames.
  3. Add a unit test asserting assertJobviteHost(buildFeedUrl(eid)) and assertJobviteHost(buildBoardFetchUrl(slug)) do not throw for representative inputs.

Example fix

// before — passing raw entry input
assertJobviteHost(entry.api);

// after — construct/validate first
let u;
try { u = new URL(entry.api); } catch { throw new Error('jobvite: bad api url'); }
assertJobviteHost(u.href);
Defensive patterns

Strategy: validation

Validate before calling

// The shipped assert only ever receives constructor-built URLs.
// If you fork it onto caller input, pre-validate first:
function prevalidate(url) {
  try { return new URL(url).href; }
  catch { throw new Error(`jobvite: invalid URL: ${url}`); }
}

Type guard

/** True for a parseable absolute URL string. */
function isAbsoluteUrl(value) {
  if (typeof value !== 'string' || !value.trim()) return false;
  try { new URL(value); return true; } catch { return false; }
}

Try / catch

try {
  return await jobviteProvider.fetch(entry, ctx);
} catch (err) {
  if (/jobvite: invalid URL/.test(err.message)) {
    // Only reachable if a constructed URL was malformed or caller input bypassed construction.
    console.error(`jobvite: ${err.message} — pass only URL-constructed strings to assertJobviteHost`);
  }
  throw err;
}

Prevention

When it happens

Trigger: A fork that passes a raw user/entry string into assertJobviteHost instead of a constructor-built URL; a hypothetical slug/eid containing characters that defeat encodeURIComponent and new URL (not observed in practice); a build mangling the host constants BOARD_HOST/FEED_HOST.

Common situations: Extending the provider to accept an entry.api feed URL and routing it through the assert without pre-validation; editing BOARD_HOST/FEED_HOST to a malformed value.

Related errors


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