{"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":158,"sourceCode":"  id: 'icims',\n\n  detect(entry) {\n    const origin = resolveOrigin(entry);\n    return origin ? { url: searchUrl(origin, 0) } : null;\n  },\n\n  /**\n   * Walk a tenant's search pages. An entry may carry `fallback_urls`: other\n   * hosts the same tenant could be served from (scan-ats-full.mjs builds them,\n   * because the public dataset stores some tenants bare and some as a full\n   * portal subdomain). A fallback is tried only when the previous host answers\n   * 404 on its FIRST page, the one response that means \"no board here\". Any\n   * other failure (throttle, timeout, DNS, a later-page 404) is rethrown as-is,\n   * so dead-board tracking still reads it as \"unknown\", never \"dead\".\n   */\n  async fetch(entry, ctx) {\n    const primary = resolveOrigin(entry);\n    if (!primary) throw new Error(`icims: cannot derive portal origin for ${entry.name}`);\n    const origins = [primary];\n    for (const raw of Array.isArray(entry.fallback_urls) ? entry.fallback_urls : []) {\n      // Same https + *.icims.com gate as the primary: a fallback can never\n      // point the scanner at another host.\n      const origin = resolveOrigin({ careers_url: raw });\n      if (origin && !origins.includes(origin)) origins.push(origin);\n    }\n    let notFound;\n    for (const origin of origins) {\n      try {\n        return await fetchPortal(origin, entry, ctx);\n      } catch (err) {\n        if (err?.status !== 404 || !err.firstPage) throw err;\n        notFound = err;\n      }\n    }\n    throw notFound;\n  },","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/santifer/career-ops/blob/e7abd431fce9348a95261acac9e0c14779c35df8/providers/icims.mjs#L140-L176","documentation":"The icims provider derives the portal origin (the tenant's *.icims.com base URL) from the portals.yml entry via resolveOrigin. If it cannot derive an origin, fetch() throws this error naming the entry, because without a base origin there is no search endpoint to page through.","triggerScenarios":"resolveOrigin(entry) returns falsy for an entry — the entry lacks the field icims derives the origin from (e.g. careers_url/portal host), or the URL does not match the expected iCIMS host pattern.","commonSituations":"A portals.yml entry was added with a plain careers page URL instead of the iCIMS tenant URL; the company migrated off iCIMS so the URL no longer matches the iCIMS pattern; a typo in the portal hostname.","solutions":["Set the entry's careers_url (or the field resolveOrigin reads) to the company's iCIMS tenant URL, e.g. https://<tenant>.icims.com/jobs","Find the iCIMS portal link from the company's careers page and update portals.yml","Remove or disable the entry if the company no longer uses iCIMS","Extend resolveOrigin only if you control the entry shape — prefer fixing the config"],"exampleFix":"// before (portals.yml)\ncareers_url: \"https://acme.com/careers\"\n// after\ncareers_url: \"https://acme.icims.com/jobs\"","handlingStrategy":"validation","validationCode":"function hasIcimsOrigin(entry) {\n  if (!entry || typeof entry.name !== 'string') return false;\n  const url = typeof entry.careers_url === 'string' ? entry.careers_url : '';\n  try {\n    const u = new URL(url);\n    return /(^|\\.)icims\\.com$/.test(u.hostname) || typeof entry.portal === 'string';\n  } catch { return false; }\n}\n// filter entries before fetch: entries.filter(hasIcimsOrigin)","typeGuard":"const isIcimsEntry = (e) => !!e && typeof e.careers_url === 'string'\n  && (() => { try { return new URL(e.careers_url).hostname.endsWith('.icims.com'); } catch { return false; } })();","tryCatchPattern":"try {\n  await icimsProvider.fetch(entry, ctx);\n} catch (err) {\n  if (/cannot derive portal origin/.test(err.message)) {\n    console.warn(`fixing config for ${entry.name}: ${err.message}`);\n    return [];\n  }\n  throw err;\n}","preventionTips":["Store the iCIMS tenant URL (https://<tenant>.icims.com/...) in careers_url","Verify each new portals.yml entry resolves before committing it","Re-audit entries after companies announce ATS migrations","Use audit-portals.mjs to catch entries whose provider can no longer claim them"],"tags":["config","url","portal","provider"],"backgroundTag":"missing-required-config-field","analyzedSha":"e7abd431fce9348a95261acac9e0c14779c35df8","analyzedAt":"2026-09-22T13:19:01.448Z","contentChangedAt":"2026-09-22T13:19:01.448Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}