santifer/career-ops · error

ashby: URL must use HTTPS

Error message

ashby: URL must use HTTPS: ${url}

What it means

After a URL parses successfully, assertAshbyUrl rejects any URL whose protocol is not https. The Ashby provider deliberately forbids http:// (and any other scheme) so credentials and board data are never sent over plaintext and no downgrade redirect tricks are possible.

Solutions

  1. Change the URL scheme to `https://` in the portals.yml entry or calling code.
  2. If the target only serves http, it is unsupported — find the canonical HTTPS Ashby board URL (`https://jobs.ashby.co/<org>` or the `jobs.ashbyhq.com` endpoint).
  3. Pre-normalize inputs: `if (url.startsWith('http://')) url = 'https://' + url.slice(7)` only when you control the source and know HTTPS is served.
  4. Check for double-scheme mistakes like `https://http://...` produced by concatenation.

Example fix

// before
assertAshbyUrl('http://jobs.ashby.co/exampleco'); // throws

// after
assertAshbyUrl('https://jobs.ashby.co/exampleco'); // ok
Defensive patterns

Strategy: validation

Validate before calling

function isHttpsUrl(url) {
  try { return new URL(url).protocol === 'https:'; } catch { return false; }
}

Type guard

function asHttpsUrl(value) {
  const u = new URL(value); // caller ensures parseable
  return u.protocol === 'https:' ? u : null;
}

Try / catch

try {
  assertAshbyUrl(url);
} catch (err) {
  if (/must use HTTPS/.test(err.message)) {
    url = url.replace(/^http:/, 'https:');
    assertAshbyUrl(url);
  } else throw err;
}

Prevention

When it happens

Trigger: Calling assertAshbyUrl with a parsed-valid URL using `http://` (e.g. `http://jobs.ashby.co/...`), or any other scheme like `ftp://` or a custom scheme that still parses.

Common situations: Old or hand-copied config entries pointing at plain http, internal proxy-style URLs like `http://localhost:8080`, or a config where the scheme got stripped/mangled by a YAML parser or string templating.

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/0023ca7b811f9fe6. Report an issue: GitHub.

Appendix: source

Thrown at providers/ashby.mjs:91

  const resolvedMax = /** @type {number} */ (max ?? min);
  return {
    min: Math.min(resolvedMin, resolvedMax),
    max: Math.max(resolvedMin, resolvedMax),
    currency: currency.toUpperCase(),
  };
}

const ALLOWED_ASHBY_HOSTS = new Set(['api.ashbyhq.com']);

/** @param {string} url */
function assertAshbyUrl(url) {
  let parsed;
  try {
    parsed = new URL(url);
  } catch {
    throw new Error(`ashby: invalid URL: ${url}`);
  }
  if (parsed.protocol !== 'https:') throw new Error(`ashby: URL must use HTTPS: ${url}`);
  if (!ALLOWED_ASHBY_HOSTS.has(parsed.hostname))
    throw new Error(`ashby: untrusted hostname "${parsed.hostname}" — must be one of: ${[...ALLOWED_ASHBY_HOSTS].join(', ')}`);
  return url;
}

/** @param {import('./_types.js').PortalEntry} entry */
function resolveApiUrl(entry) {
  // Explicit api: wins — lets an entry keep a human-facing corporate
  // careers_url (e.g. https://openai.com/careers) while still pinning the
  // Ashby posting-api board (mirrors greenhouse's api: precedence).
  if (entry.api) {
    assertAshbyUrl(entry.api);
    return entry.api;
  }
  const url = entry.careers_url || '';
  const match = url.match(/jobs\.ashbyhq\.com\/([^/?#]+)/);
  if (!match) return null;
  return `https://api.ashbyhq.com/posting-api/job-board/${match[1]}?includeCompensation=true`;

View on GitHub (pinned to aac998c7ed)