santifer/career-ops · error · Error
tkms: cannot resolve jobs host for
Error message
tkms: cannot resolve jobs host for ${entry.name} What it means
The tkms provider resolves each portal entry to a concrete jobs-host configuration via resolveConfig(entry). When resolution returns null — the entry doesn't carry the URL fields resolveConfig needs — fetch() refuses to proceed. The provider only knows how to talk to a resolvable TKMS jobs host; it will not guess one from a name.
Solutions
- Add or fix the api/careers URL on the entry so resolveConfig() succeeds — check providers/tkms.mjs for the exact expected shape.
- Compare your entry against a known-good TKMS entry in portals.yml and copy its field layout.
- Confirm the company still uses a TKMS jobs host by opening their careers page; if they migrated ATS, switch the entry to the matching provider.
- Log resolveConfig(entry) inputs in a quick REPL to see which field is missing or malformed.
Example fix
// before (portals.yml — no resolvable host) - name: SomeCompany provider: tkms // after - name: SomeCompany provider: tkms careers_url: https://somecompany.com/api/jobs
Defensive patterns
Strategy: validation
Validate before calling
function tkmsEntryIsConfigured(entry) {
const url = entry.api ?? entry.careers_url;
return typeof url === 'string' && /^https:\/\//.test(url);
}
// pre-flight: portals.filter(e => e.provider === 'tkms' && !tkmsEntryIsConfigured(e)) → fix config Type guard
const hasResolvableTkmsHost = (entry) =>
entry != null
&& typeof entry.api === 'string'
&& entry.api.startsWith('https://'); Try / catch
try {
return await tkmsProvider.fetch(entry, ctx);
} catch (err) {
if (String(err.message).startsWith('tkms: cannot resolve jobs host')) {
console.warn(`${entry.name}: missing/invalid TKMS api URL — fix portals.yml and re-run`);
return null;
}
throw err;
} Prevention
- Copy the field layout from a known-good TKMS entry when adding new companies
- Run a pre-flight config validation pass over portals.yml before scheduled scans
- Re-check companies periodically — ATS migrations silently invalidate the host pattern
- Name config fields exactly as the provider reads them (api/careers_url), not generic 'url'
When it happens
Trigger: fetch(entry, ctx) runs resolveConfig(entry) and gets null: the entry lacks a valid api/careers URL string (resolveConfig({ api: url }) fails) or the expected config fields are missing/mistyped, so it throws 'cannot resolve jobs host for <entry.name>'.
Common situations: A portals.yml entry for a TKMS company with no careers_url/api field; a URL typed with a typo that fails the resolver's validation; an entry using a field name the provider doesn't read (e.g. 'url' instead of 'api'); a company that migrated off TKMS so the host pattern no longer matches.
Understand the failure class
Background: "missing required config value" errors: why libraries refuse to start when a configuration key is empty, unset, or blank — this error's family across 48 libraries.
Related errors
- arbeitnow: invalid URL
- arbeitsagentur: entry
- ashby: cannot derive API URL for
- avature: cannot resolve origin for
- bamboohr: cannot derive API URL for
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/cade3c5985fa0533.
Report an issue: GitHub.
Appendix: source
Thrown at providers/tkms.mjs:157
function resolveMaxPages(entry) {
const v = entry?.max_pages;
if (Number.isInteger(v) && v > 0) return Math.min(v, MAX_PAGES);
return MAX_PAGES;
}
/** @type {Provider} */
export default {
id: 'tkms',
detect(entry) {
const url = entry.api || entry.careers_url || '';
if (typeof url !== 'string') return null;
return resolveConfig({ api: url }) ? { url } : null;
},
async fetch(entry, ctx) {
const cfg = resolveConfig(entry);
if (!cfg) throw new Error(`tkms: cannot resolve jobs host for ${entry.name}`);
const wait = (ms) => (ctx.sleep ? ctx.sleep(ms) : new Promise((r) => setTimeout(r, ms)));
const maxPages = resolveMaxPages(entry);
const jobs = [];
const seen = new Set();
for (let page = 0; page < maxPages; page++) {
if (page > 0) await wait(PAGE_DELAY_MS);
const json = await ctx.fetchJson(cfg.queryApi, {
method: 'POST',
redirect: 'error',
headers: { 'content-type': 'application/json', accept: 'application/json' },
body: JSON.stringify({ searchQuery: '', filter: {}, subclient: cfg.subclient, locale: cfg.locale, page }),
});
const { nextPage, rows } = parseQuery(json, cfg);
if (rows.length === 0) break;
let fresh = 0;View on GitHub (pinned to aac998c7ed)