{"record":{"id":"7561a8ca1efb0bfc","repo":"santifer/career-ops","slug":"deutschebahn-cannot-resolve-db-jobs-search-id-for","errorCode":null,"errorMessage":"deutschebahn: cannot resolve db.jobs search id for ${entry.name}","messagePattern":"deutschebahn: cannot resolve db\\.jobs search id for (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/deutschebahn.mjs","lineNumber":112,"sourceCode":"function resolveMaxPages(entry) {\n  const v = entry?.max_pages;\n  if (Number.isInteger(v) && v > 0) return Math.min(v, MAX_PAGES);\n  return MAX_PAGES;\n}\n\n/** @type {Provider} */\nexport default {\n  id: 'deutschebahn',\n\n  detect(entry) {\n    const url = entry.api || entry.careers_url || '';\n    if (typeof url !== 'string') return null;\n    return resolveConfig({ api: url }) ? { url } : null;\n  },\n\n  async fetch(entry, ctx) {\n    const cfg = resolveConfig(entry);\n    if (!cfg) throw new Error(`deutschebahn: cannot resolve db.jobs search id for ${entry.name}`);\n\n    const wait = (ms) => (ctx.sleep ? ctx.sleep(ms) : new Promise((r) => setTimeout(r, ms)));\n    const maxPages = resolveMaxPages(entry);\n    const jobs = [];\n    const seen = new Set();\n\n    for (let page = 0; page < maxPages; page++) {\n      if (page > 0) await wait(PAGE_DELAY_MS);\n      const url = `${cfg.searchBase}?qli=true&query=&sort=score&itemsPerPage=${ITEMS_PER_PAGE}&pageNum=${page}`;\n      const html = await ctx.fetchText(url, { headers: { accept: 'text/html' } });\n      const rows = parseHits(html, cfg.origin);\n      if (rows.length === 0) break; // past the last page\n\n      let fresh = 0;\n      for (const row of rows) {\n        if (seen.has(row.id)) continue;\n        seen.add(row.id);\n        fresh++;","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/santifer/career-ops/blob/9b17a8ac97b398a496b38e423ae24e433b43254f/providers/deutschebahn.mjs#L94-L130","documentation":"Thrown by the deutschebahn provider's fetch() when resolveConfig(entry) returns null. resolveConfig needs an http(s) URL whose host is exactly db.jobs or ends in .db.jobs. (It falls back to a well-known search id when the path lacks one, so a missing search id is NOT the cause — the failure is host/URL resolution, despite the message mentioning the search id.)","triggerScenarios":"entry.api/entry.careers_url is missing, unparseable, uses a non-http(s) protocol, or its host is not db.jobs / *.db.jobs. The branded jobs.deutschebahngroup.careers host redirects into db.jobs but is itself on a different domain, so pointing at the branded host directly can fail detection.","commonSituations":"Configuring the branded jobs.deutschebahngroup.careers URL (which 302-redirects into db.jobs) instead of the canonical db.jobs URL; an http-vs-https mix-up; omitting the URL entirely.","solutions":["Set api: (or careers_url:) to the canonical db.jobs search URL, e.g. https://db.jobs/service/search/de-de/5441588.","If you only have the branded URL, follow its redirect once to capture the underlying db.jobs URL and configure that.","Gate with provider.detect(entry) (returns null when resolveConfig does) before fetch()."],"exampleFix":"# before — branded host, not db.jobs\n- name: Deutsche Bahn\n  provider: deutschebahn\n  careers_url: https://jobs.deutschebahngroup.careers\n\n# after — canonical db.jobs URL\n- name: Deutsche Bahn\n  provider: deutschebahn\n  api: https://db.jobs/service/search/de-de/5441588","handlingStrategy":"validation","validationCode":"import deutschebahn from './providers/deutschebahn.mjs';\nif (!deutschebahn.detect(entry)) {\n  // entry.api/entry.careers_url is not http(s)://(*.)db.jobs/... — set it to the db.jobs URL\n}","typeGuard":"/** True when entry resolves to a db.jobs host (the failure is host resolution, not the search id). */\nfunction isDeutscheBahnEntry(entry) {\n  const raw = typeof entry?.api === 'string' ? entry.api : (typeof entry?.careers_url === 'string' ? entry.careers_url : '');\n  if (!raw) return false;\n  let u;\n  try { u = new URL(raw); } catch { return false; }\n  if (u.protocol !== 'https:' && u.protocol !== 'http:') return false;\n  const host = u.host.toLowerCase();\n  return host === 'db.jobs' || host.endsWith('.db.jobs');\n}","tryCatchPattern":"try { await deutschebahn.fetch(entry, ctx); }\ncatch (e) {\n  if (/^deutschebahn: cannot resolve db.jobs search id/.test(e.message)) {\n    // host/URL resolution failed — set api: to https://db.jobs/service/search/de-de/5441588; do not retry\n  } else throw e;\n}","preventionTips":["Use the canonical db.jobs URL, not the branded jobs.deutschebahngroup.careers redirect.","The search id defaults to a known value, so the fix is the host, not the id.","Run detect() before fetch()."],"tags":["config","deutschebahn","ats","url-resolution","db-jobs"],"backgroundTag":null,"analyzedSha":"9b17a8ac97b398a496b38e423ae24e433b43254f","analyzedAt":"2026-08-13T00:48:39.135Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}