santifer/career-ops · error · Error
pinpoint: invalid URL
Error message
pinpoint: invalid URL: ${url} What it means
assertPinpointUrl validates URLs before contacting a Pinpoint HQ tenant. The input string could not be parsed by the URL constructor, so it throws before the protocol and hostname checks. The URL is not a well-formed absolute URL at all.
Solutions
- Set careers_url to a full absolute URL: https://<slug>.pinpointhq.com in portals.yml.
- Quote the YAML scalar and re-check indentation so the full string lands in one value.
- Sanitize the value: strip whitespace/invisible characters; verify with new URL(value) in a node -e one-liner.
- If the URL is constructed programmatically, fix the builder to always include the https:// scheme.
Example fix
// before (portals.yml) careers_url: acme.pinpointhq.com // after careers_url: https://acme.pinpointhq.com
Defensive patterns
Strategy: validation
Validate before calling
export function isWellFormedPinpointUrl(u) {
if (typeof u !== 'string' || u.trim() === '') return false;
try { const parsed = new URL(u); return parsed.protocol === 'https:'; } catch { return false; }
}
if (!isWellFormedPinpointUrl(entry.careers_url)) throw new Error(`pinpoint: careers_url for ${entry.name} is not a valid absolute https URL`); Type guard
function isPinpointUrl(u) {
if (typeof u !== 'string') return false;
try {
const parsed = new URL(u);
return parsed.protocol === 'https:' && /^[a-z0-9-]+\.pinpointhq\.com$/.test(parsed.hostname);
} catch { return false; }
} Try / catch
try {
await pinpointProvider.fetch(entry, ctx);
} catch (e) {
if (String(e.message).startsWith('pinpoint: invalid URL')) {
logger.warn({ entry: entry.name, url: entry.careers_url }, 'unparseable careers_url — add https:// scheme, check YAML quoting');
return null;
}
throw e;
} Prevention
- Always include https:// in careers_url values in portals.yml.
- Quote YAML scalars with colons; avoid manual line wraps inside URLs.
- Add a load-time lint that new URL()'s every careers_url and fails fast on bad input.
- Strip whitespace/invisible characters when pasting URLs into config.
When it happens
Trigger: Calling fetch or validation paths reaching assertPinpointUrl (pinpoint.mjs line 30) with a careers_url like 'acme.pinpointhq.com' (no scheme), an empty/undefined string, or a string with characters illegal in URLs.
Common situations: portals.yml value missing https://; YAML quoting issues (colon-containing URL truncated or mangled); copy/paste introducing zero-width characters or line breaks into the scalar.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
- personio: invalid URL
- pinpoint: untrusted hostname
- pinpoint: URL must use HTTPS
- arbeitsagentur: entry
- Cannot read benchmarks at
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/e2d4580ad10a6ef4.
Report an issue: GitHub.
Appendix: source
Thrown at providers/pinpoint.mjs:30
//
// Per-tenant subdomains are the variable part — SSRF defence uses a regex
// match on `<safe-slug>.pinpointhq.com` rather than a static allowlist, the
// same approach as the recruitee provider.
// The tenant label must be a valid DNS label: it may contain hyphens but must
// not start or end with one (so `acme-.pinpointhq.com` is rejected). The
// optional trailing group keeps single-character labels (e.g. `a.pinpointhq.com`)
// valid. detect() and fetch() both route through this constant via
// resolveApiUrl()/assertPinpointUrl(), so the stricter check applies everywhere.
const PINPOINT_HOST_RE = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.pinpointhq\.com$/;
/** @param {string} url */
function assertPinpointUrl(url) {
let parsed;
try {
parsed = new URL(url);
} catch {
throw new Error(`pinpoint: invalid URL: ${url}`);
}
if (parsed.protocol !== 'https:') throw new Error(`pinpoint: URL must use HTTPS: ${url}`);
if (!PINPOINT_HOST_RE.test(parsed.hostname)) {
throw new Error(`pinpoint: untrusted hostname "${parsed.hostname}" — must match <slug>.pinpointhq.com`);
}
return url;
}
function resolveApiUrl(entry) {
const raw = typeof entry.careers_url === 'string' ? entry.careers_url : '';
if (!raw) return null;
let parsed;
try {
parsed = new URL(raw);
} catch {
return null;
}
if (parsed.protocol !== 'https:') return null;View on GitHub (pinned to aac998c7ed)