santifer/career-ops · error · Error

jibeapply: cannot derive API URL for ${entry.name}

Error message

jibeapply: cannot derive API URL for ${entry.name}

What it means

Thrown by jibeapply fetch() when neither an explicit entry.api (validated by validateExplicitApi, which requires https) nor a derivable API URL from careers_url (toApiUrl requires https AND a hostname matching ^[a-z0-9-]+\.jibeapply\.com$) yields a usable endpoint. fetch() prefers entry.api so branded/iCIMS-hosted JibeApply sites work, then falls back to deriving from a jibeapply.com careers_url; both failing means no API endpoint is reachable.

Source

Thrown at providers/jibeapply.mjs:98

  id: 'jibeapply',

  detect(entry) {
    const url = entry.careers_url;
    if (typeof url !== 'string') return null;
    const apiUrl = toApiUrl(url);
    if (!apiUrl) return null;
    return { url: apiUrl };
  },

  async fetch(entry, ctx) {
    const url = entry.careers_url;
    if (typeof url !== 'string' || !url) throw new Error('jibeapply: careers_url required');

    // Prefer an explicit entry.api (allows iCIMS-hosted, branded JibeApply sites
    // that share the same JSON schema but aren't on jibeapply.com).
    const apiUrl = (typeof entry.api === 'string' && validateExplicitApi(entry.api))
      || toApiUrl(url);
    if (!apiUrl) throw new Error(`jibeapply: cannot derive API URL for ${entry.name}`);
    const first = await ctx.fetchJson(apiUrl, { redirect: 'error' });
    const total = first.totalCount ?? 0;
    // Use the actual number of items returned as page size — some implementations
    // set `count` to the total rather than the per-page count.
    const pageSize = first.jobs?.length || first.count || DEFAULT_PAGE_SIZE;
    const allJobs = [...(first.jobs ?? [])];

    if (total > pageSize && pageSize > 0) {
      const maxPages = resolveMaxPages(entry);
      const pages = Math.min(Math.ceil(total / pageSize), maxPages);
      // Sequential, not concurrent (mirrors providers/4dayweek.mjs, thehub.mjs,
      // arbeitnow.mjs, workday.mjs) — a single tenant's API has no reason to
      // receive a burst of parallel requests, and a mid-run failure stops
      // cleanly with whatever pages were already gathered instead of
      // discarding them (Promise.all would fail the whole batch on one error).
      for (let page = 2; page <= pages; page++) {
        const u2 = new URL(apiUrl);
        u2.searchParams.set('page', String(page));

View on GitHub (pinned to 9b17a8ac97)

Solutions

  1. For branded (non-jibeapply.com) JibeApply sites, set both careers_url (any non-empty string, see error 210) AND api: to the https JSON endpoint (e.g. https://careers.acme.com/api/jobs).
  2. For standard tenants, ensure careers_url is the https://<slug>.jibeapply.com form so toApiUrl can derive /api/jobs.
  3. Confirm api: uses https: — validateExplicitApi rejects anything else.
  4. Verify with detect(): if provider.detect(entry) returns null and you have no valid entry.api, fetch() will throw this.

Example fix

# before (branded domain, no api)
- name: Acme
  provider: jibeapply
  careers_url: https://careers.acme.com/jobs

# after
- name: Acme
  provider: jibeapply
  careers_url: https://careers.acme.com/jobs
  api: https://careers.acme.com/api/jobs
Defensive patterns

Strategy: validation

Validate before calling

// Mirror fetch()'s resolution: try explicit api (https), then derive from jibeapply.com.
function resolveJibeapplyApi(entry) {
  if (typeof entry.api === 'string') {
    try { const u = new URL(entry.api); if (u.protocol === 'https:') return u.href; } catch {}
  }
  if (typeof entry.careers_url === 'string') {
    try {
      const u = new URL(entry.careers_url);
      if (u.protocol === 'https:' && /^[a-z0-9-]+\.jibeapply\.com$/i.test(u.hostname)) {
        return new URL('/api' + u.pathname, u.origin).href;
      }
    } catch {}
  }
  return null;
}

Type guard

/** True when a JibeApply API endpoint is derivable from the entry. */
function isJibeapplyDerivable(entry) {
  return resolveJibeapplyApi(entry) !== null;
}

Try / catch

try {
  const jobs = await jibeapplyProvider.fetch(entry, ctx);
} catch (err) {
  if (/cannot derive API URL/.test(err.message)) {
    console.error(`config: ${entry.name} — ${err.message} (set api: https://... or a *.jibeapply.com careers_url)`);
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: careers_url is on a branded custom domain (not *.jibeapply.com) AND entry.api is missing, non-https, or malformed; entry.api is set to an http URL (validateExplicitApi rejects non-https); careers_url hostname has an invalid character for the jibeapply.com regex.

Common situations: An iCIMS-acquired JibeApply tenant running on a branded domain where someone set careers_url to the branded URL but forgot the matching api: field; setting api: to a plain-http internal endpoint; a typo in the careers_url host breaking the regex.

Related errors


AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13). Data as JSON: /api/errors/0317533d81d9e66b. Report an issue: GitHub.