{"record":{"id":"4138ddbc8a79d767","repo":"santifer/career-ops","slug":"jobbankca-invalid-url-url","errorCode":null,"errorMessage":"jobbankca: invalid URL: ${url}","messagePattern":"jobbankca: invalid URL: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/jobbankca.mjs","lineNumber":98,"sourceCode":"  const keywords = Array.isArray(cfg.keywords)\n    ? cfg.keywords.filter((k) => typeof k === 'string' && k.trim()).map((k) => k.trim())\n    : [];\n  return { keywords };\n}\n\n/** @param {string} keyword @param {number} page */\nexport function buildFeedUrl(keyword, page) {\n  const params = new URLSearchParams({ searchstring: keyword, locationstring: '', page: String(page) });\n  return `${FEED_URL}?${params.toString()}`;\n}\n\n/** @param {string} url */\nexport function assertJobBankUrl(url) {\n  let parsed;\n  try {\n    parsed = new URL(url);\n  } catch {\n    throw new Error(`jobbankca: invalid URL: ${url}`);\n  }\n  if (parsed.protocol !== 'https:') throw new Error(`jobbankca: URL must use HTTPS: ${url}`);\n  if (parsed.hostname !== TRUSTED_HOST) {\n    throw new Error(`jobbankca: untrusted hostname \"${parsed.hostname}\" — must be ${TRUSTED_HOST}`);\n  }\n  return url;\n}\n\n// Resolve an Atom element's inner text: unwrap CDATA, else decode entities.\nfunction extractText(inner) {\n  const cdata = inner.match(/^\\s*<!\\[CDATA\\[([\\s\\S]*?)\\]\\]>\\s*$/);\n  if (cdata) return cdata[1].trim();\n  return decodeEntities(inner).trim();\n}\n\nfunction tagText(block, tag) {\n  const m = block.match(new RegExp(`<${tag}\\\\b[^>]*>([\\\\s\\\\S]*?)</${tag}>`, 'i'));\n  return m ? extractText(m[1]) : '';","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/santifer/career-ops/blob/1696bec4d021768e7359f9aad6b329cba883da20/providers/jobbankca.mjs#L80-L116","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Print the exact url value being passed and fix the malformed string in the provider config (e.g. add the https:// scheme)","Ensure the config key/env var feeding the URL is actually set (an undefined variable renders as the literal string 'undefined')","Use new URL(url) yourself in a try/catch to validate before calling, and log the cause","If building URLs programmatically, use new URL('https://...', base) or URLSearchParams instead of string concatenation"],"exampleFix":"// before\nfetchFeed(process.env.JOBBANK_FEED) // 'undefined'\n// after\nconst feedUrl = process.env.JOBBANK_FEED ?? 'https://jobbank.canada.ca/atom.xml';\nnew URL(feedUrl); // throws early with a clearer cause if still bad\nfetchFeed(feedUrl);","handlingStrategy":"validation","validationCode":"function isValidUrl(u) { try { new URL(u); return true; } catch { return false; } }\nif (!isValidUrl(cfg.url)) throw new Error(`config: not a valid URL: ${cfg.url}`);","typeGuard":"function isHttpUrl(v) {\n  if (typeof v !== 'string') return false;\n  try { const u = new URL(v); return !!u.protocol && !!u.hostname; } catch { return false; }\n}","tryCatchPattern":"try {\n  assertJobBankUrl(url);\n} catch (e) {\n  if (e.message.includes('invalid URL')) {\n    console.error(`Bad feed URL in config: ${url}`);\n    return; // skip entry, don't crash the scan\n  }\n  throw e;\n}","preventionTips":["Always store absolute URLs with explicit https:// in portals.yml","Interpolate env vars with defaults so 'undefined' never becomes part of a URL","Run new URL() on config values at load time, not fetch time","Avoid string-concatenating URLs; use the URL constructor with a base"],"tags":["url-validation","config","jobbankca"],"backgroundTag":"invalid-url","analyzedSha":"1696bec4d021768e7359f9aad6b329cba883da20","analyzedAt":"2026-09-01T19:19:23.111Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}