santifer/career-ops · error · Error

jobbankca: invalid URL: ${url}

Error message

jobbankca: invalid URL: ${url}

What it means

jobbankca's assertJobBankUrl validates every URL before it is fetched. It first attempts to parse the string with the URL constructor; if parsing fails entirely (malformed URL), it throws this error. This is a fail-fast guard so downstream fetch logic only ever sees a structurally valid URL.

Source

Thrown at providers/jobbankca.mjs:98

  const keywords = Array.isArray(cfg.keywords)
    ? cfg.keywords.filter((k) => typeof k === 'string' && k.trim()).map((k) => k.trim())
    : [];
  return { keywords };
}

/** @param {string} keyword @param {number} page */
export function buildFeedUrl(keyword, page) {
  const params = new URLSearchParams({ searchstring: keyword, locationstring: '', page: String(page) });
  return `${FEED_URL}?${params.toString()}`;
}

/** @param {string} url */
export function assertJobBankUrl(url) {
  let parsed;
  try {
    parsed = new URL(url);
  } catch {
    throw new Error(`jobbankca: invalid URL: ${url}`);
  }
  if (parsed.protocol !== 'https:') throw new Error(`jobbankca: URL must use HTTPS: ${url}`);
  if (parsed.hostname !== TRUSTED_HOST) {
    throw new Error(`jobbankca: untrusted hostname "${parsed.hostname}" — must be ${TRUSTED_HOST}`);
  }
  return url;
}

// Resolve an Atom element's inner text: unwrap CDATA, else decode entities.
function extractText(inner) {
  const cdata = inner.match(/^\s*<!\[CDATA\[([\s\S]*?)\]\]>\s*$/);
  if (cdata) return cdata[1].trim();
  return decodeEntities(inner).trim();
}

function tagText(block, tag) {
  const m = block.match(new RegExp(`<${tag}\\b[^>]*>([\\s\\S]*?)</${tag}>`, 'i'));
  return m ? extractText(m[1]) : '';

View on GitHub (pinned to 1696bec4d0)

Solutions

  1. Print the exact url value being passed and fix the malformed string in the provider config (e.g. add the https:// scheme)
  2. Ensure the config key/env var feeding the URL is actually set (an undefined variable renders as the literal string 'undefined')
  3. Use new URL(url) yourself in a try/catch to validate before calling, and log the cause
  4. If building URLs programmatically, use new URL('https://...', base) or URLSearchParams instead of string concatenation

Example fix

// before
fetchFeed(process.env.JOBBANK_FEED) // 'undefined'
// after
const feedUrl = process.env.JOBBANK_FEED ?? 'https://jobbank.canada.ca/atom.xml';
new URL(feedUrl); // throws early with a clearer cause if still bad
fetchFeed(feedUrl);
Defensive patterns

Strategy: validation

Validate before calling

function isValidUrl(u) { try { new URL(u); return true; } catch { return false; } }
if (!isValidUrl(cfg.url)) throw new Error(`config: not a valid URL: ${cfg.url}`);

Type guard

function isHttpUrl(v) {
  if (typeof v !== 'string') return false;
  try { const u = new URL(v); return !!u.protocol && !!u.hostname; } catch { return false; }
}

Try / catch

try {
  assertJobBankUrl(url);
} catch (e) {
  if (e.message.includes('invalid URL')) {
    console.error(`Bad feed URL in config: ${url}`);
    return; // skip entry, don't crash the scan
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling assertJobBankUrl(url) (directly or via a fetch that passes a configured URL) with a string that is not a parseable absolute URL — e.g. missing scheme, whitespace, 'jobbank_CANADA' as URL, an empty string, or a template literal that interpolated to garbage.

Common situations: Config typo in portals.yml (missing 'https://'), env var for a feed URL unset so 'undefined' gets interpolated into the URL string, or a relative path like '/search?f=...' passed where an absolute URL is required.

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


AI-assisted analysis of santifer/career-ops@1696bec4d0 (2026-09-01). Data as JSON: /api/errors/4138ddbc8a79d767. Report an issue: GitHub.