{"record":{"id":"6eb2798e1245084d","repo":"santifer/career-ops","slug":"icims-cannot-derive-portal-origin-for-entry-nam","errorCode":null,"errorMessage":"icims: cannot derive portal origin for ${entry.name}","messagePattern":"icims: cannot derive portal origin for (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/icims.mjs","lineNumber":114,"sourceCode":"      location: location ? decodeEntities(location[1].replace(/\\s+/g, ' ').trim()) : '',\n      // no postedAt — iCIMS list pages have no date; see enrichDate.\n    });\n  }\n  return jobs;\n}\n\n/** @type {Provider} */\nexport default {\n  id: 'icims',\n\n  detect(entry) {\n    const origin = resolveOrigin(entry);\n    return origin ? { url: searchUrl(origin, 0) } : null;\n  },\n\n  async fetch(entry, ctx) {\n    const origin = resolveOrigin(entry);\n    if (!origin) throw new Error(`icims: cannot derive portal origin for ${entry.name}`);\n    const all = [];\n    let prevFirstUrl = null;\n    // Distinguishes \"walked the whole board\" from \"stopped at the page cap\".\n    // Exhausting the cap silently would drop every later posting and look\n    // identical to a complete board — the same failure mode the Workday\n    // truncation tag exists to prevent.\n    let reachedEnd = false;\n    for (let pageNum = 0; pageNum < ICIMS_MAX_PAGES; pageNum++) {\n      if (pageNum > 0) await sleep(INTER_PAGE_DELAY_MS, ctx);\n      const html = await ctx.fetchText(searchUrl(origin, pageNum), { headers: HEADERS, redirect: 'error' });\n      const pageJobs = parseIcimsSearchPage(html, origin, entry.name);\n      if (pageJobs.length === 0) { reachedEnd = true; break; } // past the last page\n      // Some tenants serve the last real page again for an out-of-range pr\n      // instead of an empty one — a repeated first URL means we're looping.\n      if (pageJobs[0].url === prevFirstUrl) { reachedEnd = true; break; }\n      prevFirstUrl = pageJobs[0].url;\n      all.push(...pageJobs);\n    }","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/santifer/career-ops/blob/9b17a8ac97b398a496b38e423ae24e433b43254f/providers/icims.mjs#L96-L132","documentation":"Thrown by icims fetch() when resolveOrigin(entry) returns null. resolveOrigin walks [entry.api, entry.careers_url] and accepts only entries that are https URLs whose hostname ends in .icims.com, returning the origin. Anything else — wrong host, http scheme, non-URL, or both fields absent — returns null, and detect() returns null silently while fetch() throws.","triggerScenarios":"An entry tagged provider: icims whose careers_url/api is on a non-icims.com host (the company's branded domain that does not proxy iCIMS); an http:// URL (resolveOrigin requires https); a missing or non-string careers_url and no api; pasting a generic job-board URL against the icims provider.","commonSituations":"Some iCIMS tenants brand the portal onto their own domain (e.g. careers.acme.com) which still proxies icims.com on the backend but the hostname no longer ends in .icims.com; copy-pasting an http link; YAML misindentation leaving careers_url unset.","solutions":["Set careers_url to the canonical https://careers-{tenant}.icims.com/jobs/search?ss=1 form (or any https *.icims.com URL).","If the tenant uses a branded domain that is genuinely iCIMS-hosted but not on icims.com, that host is intentionally unsupported by auto-detect — confirm the icims.com backend URL and set api/careers_url to it.","Validate with detect(): provider.detect(entry) returning null means fetch() will throw this too."],"exampleFix":"# before\n- name: Acme\n  provider: icims\n  careers_url: https://careers.acme.com/jobs\n\n# after\n- name: Acme\n  provider: icims\n  careers_url: https://careers-acme.icims.com/jobs/search?ss=1","handlingStrategy":"validation","validationCode":"const hit = icimsProvider.detect(entry);\nif (!hit) {\n  console.warn(`icims: ${entry.name} — api/careers_url must be an https *.icims.com URL`);\n  continue;\n}\nconst jobs = await icimsProvider.fetch(entry, ctx);","typeGuard":"/** True when the entry can resolve an iCIMS portal origin. */\nfunction isIcimsDerivable(entry) {\n  for (const raw of [entry?.api, entry?.careers_url]) {\n    if (typeof raw !== 'string' || !raw) continue;\n    try {\n      const u = new URL(raw);\n      if (u.protocol === 'https:' && u.hostname.endsWith('.icims.com')) return true;\n    } catch { /* ignore */ }\n  }\n  return false;\n}","tryCatchPattern":"try {\n  const jobs = await icimsProvider.fetch(entry, ctx);\n} catch (err) {\n  if (/cannot derive portal origin/.test(err.message)) {\n    console.error(`config: ${entry.name} — ${err.message}`);\n  } else {\n    throw err;\n  }\n}","preventionTips":["Use the canonical https://careers-{tenant}.icims.com form for careers_url.","Branded (non-icims.com) domains are intentionally not auto-detected — find the icims.com backend URL.","Gate entries through detect() so misconfigured boards are reported, not silently empty."],"tags":["config","url-resolution","icims","ats"],"backgroundTag":null,"analyzedSha":"9b17a8ac97b398a496b38e423ae24e433b43254f","analyzedAt":"2026-08-13T00:48:39.135Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}