{"record":{"id":"407618a0f1e53401","repo":"santifer/career-ops","slug":"jibeapply-careers-url-required","errorCode":null,"errorMessage":"jibeapply: careers_url required","messagePattern":"jibeapply: careers_url required","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/jibeapply.mjs","lineNumber":99,"sourceCode":"    })\n    .filter(Boolean);\n}\n\n/** @type {Provider} */\nexport default {\n  id: 'jibeapply',\n\n  detect(entry) {\n    const url = entry.careers_url;\n    if (typeof url !== 'string') return null;\n    const apiUrl = toApiUrl(url);\n    if (!apiUrl) return null;\n    return { url: apiUrl };\n  },\n\n  async fetch(entry, ctx) {\n    const url = entry.careers_url;\n    if (typeof url !== 'string' || !url) throw new Error('jibeapply: careers_url required');\n\n    // Prefer an explicit entry.api (allows iCIMS-hosted, branded JibeApply sites\n    // that share the same JSON schema but aren't on jibeapply.com).\n    const apiUrl = (typeof entry.api === 'string' && validateExplicitApi(entry.api))\n      || toApiUrl(url);\n    if (!apiUrl) throw new Error(`jibeapply: cannot derive API URL for ${entry.name}`);\n    const first = await ctx.fetchJson(apiUrl, { redirect: 'error' });\n    const total = first.totalCount ?? 0;\n    // Use the actual number of items returned as page size — some implementations\n    // set `count` to the total rather than the per-page count.\n    const pageSize = first.jobs?.length || first.count || DEFAULT_PAGE_SIZE;\n    const allJobs = [...(first.jobs ?? [])];\n\n    if (total > pageSize && pageSize > 0) {\n      const maxPages = resolveMaxPages(entry);\n      const pages = Math.min(Math.ceil(total / pageSize), maxPages);\n      // Sequential, not concurrent (mirrors providers/4dayweek.mjs, thehub.mjs,\n      // arbeitnow.mjs, workday.mjs) — a single tenant's API has no reason to","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/providers/jibeapply.mjs#L81-L117","documentation":"The jibeapply provider reads the board URL directly from entry.careers_url. If the field is missing, not a string, or empty, fetch() throws this error because there is no URL to convert into a JibeApply API endpoint.","triggerScenarios":"fetch(entry) is called with an entry whose careers_url is undefined, null, non-string, or \"\" — typically an incomplete portals.yml entry for a jibeapply-backed company.","commonSituations":"A new company was added to portals.yml with only a name and no careers_url; YAML indentation error put careers_url under the wrong entry; the field was renamed in a config refactor.","solutions":["Add a valid careers_url to the entry in portals.yml","Verify YAML indentation so careers_url belongs to the jibeapply entry","If the site is iCIMS-hosted/branded, optionally also set entry.api with the explicit API URL","Disable/remove the entry if it is a placeholder"],"exampleFix":"// before (portals.yml)\n- name: acme\n  provider: jibeapply\n// after\n- name: acme\n  provider: jibeapply\n  careers_url: \"https://acme.jibeapply.com/jobs\"","handlingStrategy":"validation","validationCode":"function entryHasCareersUrl(entry) {\n  return typeof entry?.careers_url === 'string' && entry.careers_url.trim() !== '';\n}\n// filter before fetch: jibeapplyEntries.filter(entryHasCareersUrl)","typeGuard":"const hasCareersUrl = (e) => typeof e?.careers_url === 'string' && e.careers_url.length > 0;","tryCatchPattern":"try {\n  await jibeapplyProvider.fetch(entry, ctx);\n} catch (err) {\n  if (/careers_url required/.test(err.message)) {\n    console.warn(`jibeapply entry ${entry?.name} missing careers_url; skipping`);\n    return [];\n  }\n  throw err;\n}","preventionTips":["Require careers_url in every portals.yml entry via a config schema check","Watch YAML indentation so fields attach to the right entry","Run verify-pipeline.mjs / config lint before scans","Never add placeholder entries without at least a URL"],"tags":["config","missing-field","validation","provider"],"backgroundTag":"missing-required-config-field","analyzedSha":"aac998c7ed7248ea853b720ceeb1fdbeb322fc5d","analyzedAt":"2026-09-16T06:35:29.214Z","contentChangedAt":"2026-09-16T06:35:29.214Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}