{"record":{"id":"5722c6a123e4552b","repo":"santifer/career-ops","slug":"teamtailor-invalid-url-url","errorCode":null,"errorMessage":"teamtailor: invalid URL: ${url}","messagePattern":"teamtailor: invalid URL: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/teamtailor.mjs","lineNumber":39,"sourceCode":"// explicit `provider: teamtailor`. Either way the fetch is HTTPS-only with\n// `redirect: 'error'`. Job `<link>`s (branded domains) are emitted as-is and\n// never fetched.\n\nconst TEAMTAILOR_HOST_RE = /^([a-z0-9](?:[a-z0-9-]*[a-z0-9])?)\\.teamtailor\\.com$/i;\n\n/**\n * Validate a feed URL before fetching. Always HTTPS-only. The hostname is\n * pinned to `*.teamtailor.com` for auto-detected entries; an explicit\n * `provider: teamtailor` entry may use its configured branded host.\n * @param {string} url\n * @param {{ explicit?: boolean }} [opts]\n */\nfunction assertFeedUrl(url, { explicit = false } = {}) {\n  let parsed;\n  try {\n    parsed = new URL(url);\n  } catch {\n    throw new Error(`teamtailor: invalid URL: ${url}`);\n  }\n  if (parsed.protocol !== 'https:') throw new Error(`teamtailor: URL must use HTTPS: ${url}`);\n  if (!explicit && !TEAMTAILOR_HOST_RE.test(parsed.hostname)) {\n    throw new Error(`teamtailor: untrusted hostname \"${parsed.hostname}\" — must be <slug>.teamtailor.com (or set \"provider: teamtailor\" to use a branded careers domain)`);\n  }\n  return url;\n}\n\n// Derive the RSS feed URL from a tracked_companies entry by normalizing any\n// path on the configured host to /jobs.rss. Auto-detection (explicit=false)\n// only claims *.teamtailor.com hosts; an explicit `provider: teamtailor` entry\n// (explicit=true) may use a branded careers host. Returns null otherwise.\n/**\n * @param {import('./_types.js').PortalEntry} entry\n * @param {{ explicit?: boolean }} [opts]\n */\nfunction resolveFeedUrl(entry, { explicit = false } = {}) {\n  const raw = entry?.api || entry?.careers_url || '';","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/providers/teamtailor.mjs#L21-L57","documentation":"teamtailor's assertFeedUrl first requires the URL to parse as a valid URL. If new URL(url) throws (malformed, missing scheme, spaces, etc.), the provider throws this 'invalid URL' error naming the offending value.","triggerScenarios":"careers_url entered without a scheme (acme.teamtailor.com), with a typo (htps://), with embedded whitespace or a stray character; an env-substituted placeholder left unfilled (e.g. ${COMPANY}.teamtailor.com); an empty-ish string that slips past null checks.","commonSituations":"Hand-editing portals.yml with a missing https://; YAML folding a URL across lines; template variable not expanded; copy-paste introducing a trailing space or invisible character.","solutions":["Add https:// to the URL and remove whitespace/typos so it is a valid absolute URL","Print or log the configured value to spot invisible characters or unexpanded variables","Validate entries with node verify-pipeline.mjs before running scans"],"exampleFix":"// before\ncareers_url: 'acme.teamtailor.com/jobs.rss'\n// after\ncareers_url: 'https://acme.teamtailor.com/jobs.rss'","handlingStrategy":"validation","validationCode":"let u;\ntry { u = new URL(entry.careers_url); } catch { throw new Error(`${entry.name}: careers_url is not a valid absolute URL`); }\nif (u.protocol !== 'https:') throw new Error(`${entry.name}: careers_url must be https://`);","typeGuard":"function isParseableHttpsUrl(u) {\n  try { return new URL(u).protocol === 'https:'; } catch { return false; }\n}","tryCatchPattern":"try {\n  offers = await provider.fetch(entry, ctx);\n} catch (e) {\n  if (e.message.startsWith('teamtailor: invalid URL')) {\n    console.warn(`${entry.name}: fix careers_url — it does not parse as an absolute URL`);\n    return [];\n  }\n  throw e;\n}","preventionTips":["Always include the https:// scheme in careers_url","Paste URLs from the browser address bar, not from memory","Check for unexpanded template variables and stray whitespace","Lint portals.yml entries before committing"],"tags":["url-validation","config"],"backgroundTag":"invalid-url-format","analyzedSha":"aac998c7ed7248ea853b720ceeb1fdbeb322fc5d","analyzedAt":"2026-09-16T06:35:29.214Z","contentChangedAt":"2026-09-16T06:35:29.214Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}