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

  1. Set careers_url to a full absolute URL: https://<slug>.pinpointhq.com in portals.yml.
  2. Quote the YAML scalar and re-check indentation so the full string lands in one value.
  3. Sanitize the value: strip whitespace/invisible characters; verify with new URL(value) in a node -e one-liner.
  4. 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

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


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)