santifer/career-ops · error · Error

pinpoint: URL must use HTTPS

Error message

pinpoint: URL must use HTTPS: ${url}

What it means

assertPinpointUrl enforces HTTPS for every URL it accepts; a URL with any other scheme (http:, ftp:) is rejected with this error. Pinpoint feeds are always served over TLS, so the provider refuses non-https input up front to keep the SSRF guard and transport policy uniform.

Solutions

  1. Change the scheme to https:// in the portals.yml careers_url.
  2. Confirm the tenant serves https in a browser (all *.pinpointhq.com tenants do).
  3. Fix any URL-building template to emit https://${host}.
  4. Grep portals.yml for http:// to catch and fix all insecure scheme values at once.

Example fix

// before (portals.yml)
careers_url: http://acme.pinpointhq.com
// after
careers_url: https://acme.pinpointhq.com
Defensive patterns

Strategy: validation

Validate before calling

function isHttpsPinpointUrl(u) {
  if (typeof u !== 'string') return false;
  try { return new URL(u).protocol === 'https:'; } catch { return false; }
}
if (!isHttpsPinpointUrl(entry.careers_url)) throw new Error(`pinpoint: careers_url for ${entry.name} must use https://`);

Type guard

function isHttpsPinpointUrl(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: URL must use HTTPS')) {
    logger.warn({ entry: entry.name, url: entry.careers_url }, 'rewrite http:// to https:// in portals.yml');
    return null;
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling code paths through assertPinpointUrl (pinpoint.mjs line 32) with careers_url scheme http:// — e.g. http://acme.pinpointhq.com — or a scheme that parsed to something unexpected from a malformed string.

Common situations: Hand-authored config with http://; legacy internal links; snippets copied from docs with http; scripts that build URLs with a hardcoded http scheme.

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


AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16). Data as JSON: /api/errors/e0b31f56556d82c3. Report an issue: GitHub.

Appendix: source

Thrown at providers/pinpoint.mjs:32

// 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;
  if (!PINPOINT_HOST_RE.test(parsed.hostname)) return null;
  return `https://${parsed.hostname}/postings.json`;

View on GitHub (pinned to aac998c7ed)