{"record":{"id":"840d95ec0d7bc738","repo":"santifer/career-ops","slug":"teamtailor-cannot-derive-jobs-rss-url-for-fallb","errorCode":null,"errorMessage":"teamtailor: cannot derive jobs.rss URL for ${fallbackCompany(entry)}","messagePattern":"teamtailor: cannot derive jobs\\.rss URL for (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/teamtailor.mjs","lineNumber":86,"sourceCode":"\nfunction fallbackCompany(entry) {\n  return typeof entry?.name === 'string' && entry.name.trim() ? entry.name.trim() : 'Teamtailor';\n}\n\n/** @type {Provider} */\nexport default {\n  id: 'teamtailor',\n\n  detect(entry) {\n    // Auto-detection never claims a branded host — only *.teamtailor.com.\n    const url = resolveFeedUrl(entry, { explicit: false });\n    return url ? { url } : null;\n  },\n\n  async fetch(entry, ctx) {\n    const explicit = entry?.provider === 'teamtailor';\n    const feedUrl = resolveFeedUrl(entry, { explicit });\n    if (!feedUrl) throw new Error(`teamtailor: cannot derive jobs.rss URL for ${fallbackCompany(entry)}`);\n    assertFeedUrl(feedUrl, { explicit });\n    // redirect:'error' prevents SSRF via server-side redirects; combined with\n    // assertFeedUrl above it keeps the request pinned to the trusted host\n    // (*.teamtailor.com, or the branded host the user explicitly configured).\n    const text = await ctx.fetchText(feedUrl, { redirect: 'error' });\n    return parseTeamtailorFeed(text, fallbackCompany(entry));\n  },\n};\n\nfunction fromCodePoint(cp) {\n  try {\n    return String.fromCodePoint(cp);\n  } catch {\n    return '';\n  }\n}\n\n// Decode the XML entities that appear in RSS text: numeric (&#38; / &#x27;)","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/santifer/career-ops/blob/9b17a8ac97b398a496b38e423ae24e433b43254f/providers/teamtailor.mjs#L68-L104","documentation":"resolveFeedUrl returns null when there is no api/careers_url, the URL cannot be parsed, the protocol is not https, or — for non-explicit entries — the host is not *.teamtailor.com. fetch then throws because no /jobs.rss URL can be derived. detect() only claims *.teamtailor.com hosts, so a branded-domain entry without provider: teamtailor reaches fetch only via an explicit override.","triggerScenarios":"An explicit provider: teamtailor entry whose api/careers_url is missing, malformed, or non-https; or an auto-detected entry that detect should have rejected but fetch was called directly. Also a bare *.teamtailor.com URL with an empty path still resolves fine, so this is specifically about bad/missing input.","commonSituations":"provider: teamtailor is set on an entry but the URL line was omitted/mis-indented; the branded URL is http://; or the entry was constructed programmatically without a URL.","solutions":["Set api or careers_url to a valid https URL (canonical: https://<slug>.teamtailor.com, branded: https://careers.acme.com with provider: teamtailor)","Ensure the scheme is https://","Confirm provider: teamtailor is present if you use a branded host","If the company is not on Teamtailor, remove the provider tag"],"exampleFix":"# before\n- name: Acme\n  provider: teamtailor\n# after\n- name: Acme\n  provider: teamtailor\n  careers_url: https://careers.acme.com","handlingStrategy":"validation","validationCode":"// Run detect() first; a null result means fetch will throw 'cannot derive'.\nimport tt from './providers/teamtailor.mjs';\nif (!tt.detect(entry) && entry.provider !== 'teamtailor') {\n  console.warn(`${entry.name}: teamtailor cannot derive jobs.rss — check URL/provider`);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Call provider.detect(entry) before fetch; null predicts the throw.","For explicit provider: teamtailor entries, ensure a valid https api/careers_url is set.","Lint entries for missing/empty URL fields."],"tags":["config","teamtailor","missing-field","portals-yml","detection"],"backgroundTag":null,"analyzedSha":"9b17a8ac97b398a496b38e423ae24e433b43254f","analyzedAt":"2026-08-13T00:48:39.135Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}