santifer/career-ops · error · Error
comeet: cannot derive API URL for ${entry.name} (set api: to
Error message
comeet: cannot derive API URL for ${entry.name} (set api: to the full careers-api positions URL) What it means
The primary comeet misconfiguration error: resolveApiUrl(entry) returned null, meaning neither entry.api nor entry.careers_url is a full Comeet careers-api positions URL. Comeet's positions endpoint needs both a company uid and a per-tenant token, neither of which is derivable from a branded careers page, so the full https://www.comeet.co/careers-api/2.0/company/<uid>/positions?token=<token> URL must be supplied explicitly via the api: field. This is the error real users hit; the four assertComeetUrl errors (161-164) are pre-empted by it.
Source
Thrown at providers/comeet.mjs:88
const parsed = Date.parse(value);
return Number.isNaN(parsed) ? undefined : parsed;
}
/** @type {Provider} */
export default {
id: 'comeet',
detect(entry) {
const apiUrl = resolveApiUrl(entry);
// The DetectHit url is informational (the framework may log it), so strip
// the secret ?token= before returning it — fetch() re-resolves the real
// URL from the entry, so redaction here is safe.
return apiUrl ? { url: redactToken(apiUrl) } : null;
},
async fetch(entry, ctx) {
const apiUrl = resolveApiUrl(entry);
if (!apiUrl) throw new Error(`comeet: cannot derive API URL for ${entry.name} (set api: to the full careers-api positions URL)`);
assertComeetUrl(apiUrl);
// redirect:'error' prevents SSRF via server-side redirects; combined with
// assertComeetUrl above it guarantees the final hostname stays www.comeet.co.
const json = await ctx.fetchJson(apiUrl, { redirect: 'error' });
return parseComeetResponse(json, entry.name);
},
};
/**
* Parse a Comeet careers-api positions response. Exported for unit tests.
*
* Comeet returns a top-level ARRAY of position objects:
* [{ name, location: { name, is_remote }, url_active_page,
* url_comeet_hosted_page, time_updated, ... }]
*
* - url: prefer `url_active_page` (the tenant's live careers page), fall back to
* `url_comeet_hosted_page` (the Comeet-hosted page). Both are public, display-
* only URLs (recorded in the pipeline/history, never server-fetched here), soView on GitHub (pinned to 9b17a8ac97)
Solutions
- Set entry.api to the full positions URL from Comeet's careers-API docs, including the ?token= query param: https://www.comeet.co/careers-api/2.0/company/<uid>/positions?token=<token>.
- If you only have the branded page, obtain the careers-api URL from Comeet's admin/recruiter dashboard — the token is not scrapable from the public page.
- Gate with provider.detect(entry) (returns null when resolveApiUrl does) so misconfigured entries are skipped instead of throwing.
Example fix
# before — branded page, no token, will not resolve - name: Acme provider: comeet careers_url: https://www.comeet.com/jobs/acme # after — full careers-api positions URL with token - name: Acme provider: comeet api: https://www.comeet.co/careers-api/2.0/company/abc/positions?token=SECRET_TOKEN
Defensive patterns
Strategy: validation
Validate before calling
// detect() returns null iff resolveApiUrl does — i.e. exactly when fetch() would throw 165.
import comeet from './providers/comeet.mjs';
if (!comeet.detect(entry)) {
// entry.api is not a full https://www.comeet.co/careers-api/.../positions?token=... URL — set it
} Type guard
/** True when entry has a usable Comeet careers-api positions URL (with token). */
function isComeetEntry(entry) {
const raw = typeof entry?.api === 'string' ? entry.api : '';
if (!raw) return false;
try {
const p = new URL(raw);
return p.protocol === 'https:' && p.hostname === 'www.comeet.co' && p.pathname.startsWith('/careers-api/') && p.searchParams.has('token');
} catch { return false; }
} Try / catch
try { await comeet.fetch(entry, ctx); }
catch (e) {
if (/^comeet: cannot derive API URL/.test(e.message)) {
// config issue — set entry.api to the full positions URL; do not retry
} else throw e;
} Prevention
- Set entry.api to the full positions URL including ?token= — the branded page has no token.
- Run detect() before fetch() to skip misconfigured entries.
- The token is secret — store it in config, never log it (the provider redacts it on throw).
When it happens
Trigger: entry.api is absent and entry.careers_url is the branded www.comeet.com/jobs/... page (no token, wrong host, wrong path); or entry.api is set but is not https / not on www.comeet.co / not under /careers-api/. isComeetApiUrl returns false for both fields, so resolveApiUrl returns null.
Common situations: Copying the public careers page URL into careers_url and expecting the provider to derive the API call (it cannot — the token is secret and not on that page); pasting an http or www.comeet.com URL into api:; forgetting to migrate an old entry after Comeet renamed the endpoint.
Related errors
- breezy: cannot derive API URL for ${entry.name}
- consider: ${entry.name} needs an https careers_url on a publ
- csod: cannot resolve careersite URL for ${entry.name}
- eightfold: cannot derive API URL for ${entry.name}
- deutschebahn: cannot resolve db.jobs search id for ${entry.n
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/a5d25f84a0bea448.
Report an issue: GitHub.