santifer/career-ops · error · Error
ashby: invalid URL: ${url}
Error message
ashby: invalid URL: ${url} What it means
ashby validates every URL through `assertAshbyUrl` before fetching. The 'invalid URL' variant fires when `new URL(url)` throws — the input is not parseable as an absolute URL. This is the first of three sequential guards (parse → https → host allowlist).
Source
Thrown at providers/ashby.mjs:86
// Ensure correct ordering (min <= max)
const resolvedMin = /** @type {number} */ (min ?? max);
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\/([^/?#]+)/);View on GitHub (pinned to 9b17a8ac97)
Solutions
- Set `api:` to a fully-qualified `https://api.ashbyhq.com/...` URL.
- Validate entry URLs at config load time so the error surfaces next to the offending row.
- Check for whitespace or template interpolation producing an empty/mangled scheme.
Example fix
# before - name: Acme api: api.ashbyhq.com/posting-api?compId=acme # after - name: Acme api: https://api.ashbyhq.com/posting-api?compId=acme
Defensive patterns
Strategy: validation
Validate before calling
// Validate entry.api is an absolute URL before passing to the provider
function isValidAbsoluteUrl(u) {
try { new URL(u); return true; } catch { return false; }
}
if (entry.api && !isValidAbsoluteUrl(entry.api)) {
throw new Error(`ashby: entry ${entry.name} has a malformed api URL: ${entry.api}`);
} Prevention
- Require fully-qualified URLs in config (no schemeless hosts).
- Run a config validator that tests every entry.api with `new URL()`.
- Keep `detect()` as the first gate so malformed entries are skipped rather than fetched.
When it happens
Trigger: `assertAshbyUrl(url)` receives a string `new URL()` cannot parse: a schemeless host (`api.ashbyhq.com/...`), a protocol-relative URL, an empty string, or a value with illegal characters. Typically called on `entry.api` or a derived URL in `resolveApiUrl`.
Common situations: An entry's `api:` field is set without a scheme, a careers_url that gets parsed drops characters, or a templated/config-injected value is malformed.
Related errors
- arbeitnow: invalid URL: ${url}
- ashby: URL must use HTTPS: ${url}
- bamboohr: invalid URL: ${url}
- breezy: invalid URL: ${url}
- jobstreet: invalid URL: ${url}
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/b6589393512cbf0a.
Report an issue: GitHub.