santifer/career-ops · error · Error
teamtailor: cannot derive jobs.rss URL for
Error message
teamtailor: cannot derive jobs.rss URL for ${fallbackCompany(entry)} What it means
When an entry does not explicitly declare provider: teamtailor, the provider tries to auto-derive the feed URL (host normalized to /jobs.rss). If resolveFeedUrl returns null — the entry has no usable careers_url/host to derive from — the fetch cannot proceed and throws naming the company.
Solutions
- Set careers_url to the company's teamtailor feed URL (e.g. https://<slug>.teamtailor.com/jobs.rss)
- Or add provider: teamtailor plus the branded host URL explicitly
- Verify the company actually uses TeamTailor; otherwise switch providers
Example fix
// before - name: Acme # after - name: Acme careers_url: 'https://acme.teamtailor.com/jobs.rss'
Defensive patterns
Strategy: validation
Validate before calling
if (String(entry.provider ?? '') !== 'teamtailor' && !entry.careers_url) {
throw new Error(`${entry.name}: teamtailor auto-derivation needs careers_url`);
} Type guard
function canDeriveTTFeed(entry) {
return Boolean(entry?.provider === 'teamtailor' || (typeof entry?.careers_url === 'string' && entry.careers_url.startsWith('http')));
} Try / catch
try {
offers = await provider.fetch(entry, ctx);
} catch (e) {
if (e.message.includes('cannot derive jobs.rss URL')) {
console.warn(`${entry.name}: add a careers_url pointing at the teamtailor feed`);
return [];
}
throw e;
} Prevention
- Always fill careers_url for teamtailor entries, even when auto-detection seems possible
- Confirm the company actually hosts on TeamTailor before selecting the provider
- Keep per-provider example entries in your config templates
When it happens
Trigger: Entry missing careers_url and any URL-ish field so nothing can be normalized to /jobs.rss; careers_url is present but resolveFeedUrl's matching rejects it (empty, non-URL); entry.provider set to a different value so explicit=true logic is off while no URL exists.
Common situations: Newly added company with only a name filled in; company's careers site isn't TeamTailor-hosted so no feed can be derived; config migration dropped the url field.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- apify: entry missing 'actor' (e.g. misceres/indeed-scraper)
- arbeitsagentur: entry
- breezy: cannot derive API URL for
- gem: cannot derive board id for
- getro: needs an https careers_url
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/840d95ec0d7bc738.
Report an issue: GitHub.
Appendix: source
Thrown at providers/teamtailor.mjs:87
function fallbackCompany(entry) {
return typeof entry?.name === 'string' && entry.name.trim() ? entry.name.trim() : 'Teamtailor';
}
/** @type {Provider} */
export default {
id: 'teamtailor',
detect(entry) {
// Auto-detection never claims a branded host — only *.teamtailor.com.
const url = resolveFeedUrl(entry, { explicit: false });
return url ? { url } : null;
},
async fetch(entry, ctx) {
const explicit = entry?.provider === 'teamtailor';
const feedUrl = resolveFeedUrl(entry, { explicit });
if (!feedUrl) throw new Error(`teamtailor: cannot derive jobs.rss URL for ${fallbackCompany(entry)}`);
assertFeedUrl(feedUrl, { explicit });
// redirect:'error' prevents SSRF via server-side redirects; combined with
// assertFeedUrl above it keeps the request pinned to the trusted host
// (*.teamtailor.com, or the branded host the user explicitly configured).
const text = await ctx.fetchText(feedUrl, { redirect: 'error' });
return parseTeamtailorFeed(text, fallbackCompany(entry));
},
};
// Resolve a tag's inner text: unwrap a CDATA section, 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();
}
// Extract the text of the first <tag>...</tag> in a block. Returns '' when
// absent. Tag names may contain a namespace colon (e.g. tt:city).View on GitHub (pinned to aac998c7ed)