santifer/career-ops · error · Error
pinpoint: cannot derive API URL for
Error message
pinpoint: cannot derive API URL for ${entry.name} What it means
The pinpoint provider only serves portal entries whose careers_url matches `https://<slug>.pinpointhq.com`. resolveApiUrl() returns null when the entry has no careers_url, an unparseable URL, a non-HTTPS URL, or a hostname that does not match the tenant regex. fetch() throws this error so the caller knows the provider cannot be used for this entry rather than silently returning no jobs.
Solutions
- Set entry.careers_url to the actual tenant subdomain URL, e.g. https://<slug>.pinpointhq.com
- Verify the company still uses Pinpoint by checking the careers page; if it migrated, change the provider entry accordingly
- Ensure the URL is https: and parseable (no spaces, full scheme included)
- If the tenant uses a custom domain, find the underlying <slug>.pinpointhq.com host and use that
Example fix
// before (portals.yml) careers_url: careers.acme.com // after careers_url: https://acme.pinpointhq.com
Defensive patterns
Strategy: validation
Validate before calling
import { URL } from 'url';
const RE = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.pinpointhq\.com$/;
function resolvable(entry) {
const raw = typeof entry.careers_url === 'string' ? entry.careers_url : '';
if (!raw) return false;
try {
const u = new URL(raw);
return u.protocol === 'https:' && RE.test(u.hostname);
} catch { return false; }
} Type guard
function isPinpointEntry(entry) {
return typeof entry?.careers_url === 'string'
&& /^https:\/\/[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.pinpointhq\.com\/?$/.test(entry.careers_url);
} Try / catch
try {
await provider.fetch(entry, ctx);
} catch (err) {
if (String(err.message).startsWith('pinpoint: cannot derive API URL')) {
console.warn(`Skipping ${entry.name}: careers_url is not a Pinpoint tenant URL`);
return [];
}
throw err;
} Prevention
- Store full https://<slug>.pinpointhq.com URLs in portals.yml, never bare hostnames or custom domains
- Run a config linter that pattern-checks careers_url against the provider's host regex before scans
- When a company migrates ATS, update the careers_url and provider in the same commit
When it happens
Trigger: Calling fetch() with an entry whose careers_url is missing, empty, not a string, malformed (new URL() throws), using http: instead of https:, or on a non-pinpointhq.com hostname (e.g. a custom careers domain or a different ATS).
Common situations: A portals.yml entry lists a company that moved off Pinpoint (ATS migration) but still points at the old careers URL; a custom careers domain (careers.example.com) is used instead of the <slug>.pinpointhq.com subdomain; the URL was typoed or entered as http://.
Understand the failure class
Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.
Related errors
- himalayas: untrusted hostname
- itviec: untrusted hostname
- jobbankca: invalid URL
- jobspresso: invalid URL
- jobstreet: invalid URL
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/d700a701729c7c16.
Report an issue: GitHub.
Appendix: source
Thrown at providers/pinpoint.mjs:64
return null;
}
if (parsed.protocol !== 'https:') return null;
if (!PINPOINT_HOST_RE.test(parsed.hostname)) return null;
return `https://${parsed.hostname}/postings.json`;
}
/** @type {Provider} */
export default {
id: 'pinpoint',
detect(entry) {
const apiUrl = resolveApiUrl(entry);
return apiUrl ? { url: apiUrl } : null;
},
async fetch(entry, ctx) {
const apiUrl = resolveApiUrl(entry);
if (!apiUrl) throw new Error(`pinpoint: cannot derive API URL for ${entry.name}`);
assertPinpointUrl(apiUrl);
// redirect:'error' prevents SSRF via server-side redirects
const json = await ctx.fetchJson(apiUrl, { redirect: 'error' });
return parsePinpointResponse(json, entry.name);
},
};
/**
* Parse a Pinpoint /postings.json response. Exported for unit tests.
*
* Pinpoint returns:
* { data: [{ title, url, path, location: { name, city, province, ... }, ... }] }
*
* Field mapping → the normalized Job shape:
* - title: `title`, trimmed.
* - url: `url` — an absolute posting URL on the tenant's own
* `<slug>.pinpointhq.com` host. It is display-only (written to the
* pipeline and scan history, never server-fetched here), so it isView on GitHub (pinned to aac998c7ed)