santifer/career-ops · error · Error
a16z-speedrun-talent: untrusted hostname
Error message
a16z-speedrun-talent: untrusted hostname "${parsed.hostname}" — must be ${TRUSTED_HOST} What it means
assertFeedUrl in providers/a16z-speedrun-talent.mjs throws this when the URL is valid HTTPS but its hostname does not exactly equal the provider's TRUSTED_HOST. This is an SSRF/misconfiguration guard: the provider fetches only from its one known feed host, and any other hostname is rejected even if it is a plausible mirror or subdomain.
Solutions
- Correct the URL's hostname to exactly match TRUSTED_HOST as defined at the top of providers/a16z-speedrun-talent.mjs.
- If you meant a different feed, note that host is intentionally unsupported — remove the entry or use the official feed.
- Print new URL(url).hostname to compare character-by-character with TRUSTED_HOST (watch for 'www.' prefixes and typos).
Example fix
// before
fetchSpeedrunFeed('https://speedrun.example.com/feed')
// after
fetchSpeedrunFeed('https://a16z-speedrun.com/feed') // exact TRUSTED_HOST Defensive patterns
Strategy: validation
Validate before calling
const u = new URL(rawUrl);
if (u.hostname !== TRUSTED_HOST) throw new Error(`feed host must be ${TRUSTED_HOST}, got ${u.hostname}`); Type guard
function isTrustedSpeedrunUrl(url) {
try { const u = new URL(url); return u.protocol === 'https:' && u.hostname === TRUSTED_HOST; }
catch { return false; }
} Try / catch
try {
await fetchSpeedrunFeed(url);
} catch (e) {
if (String(e.message).includes('untrusted hostname')) {
console.error(`Config error: expected host ${TRUSTED_HOST}, got: ${url}`);
return null;
} else throw e;
} Prevention
- Keep the trusted host as the single source of truth; build feed URLs from it.
- Compare hostnames exactly — beware 'www.' prefixes and lookalike domains.
- When migrating configs between providers, update the host check, not just the URL.
- Test config URLs against TRUSTED_HOST in CI.
When it happens
Trigger: A feed URL pointing at a different domain, a subdomain variant, a staging mirror, or a typo'd host — parsed fine, https fine, but parsed.hostname !== TRUSTED_HOST.
Common situations: Config copied from another provider with a different TRUSTED_HOST; a custom portals.yml entry pointing at the company site instead of the feed host; DNS/CNAME assumptions that do not change the literal hostname string.
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
- 4dayweek: untrusted hostname
- a16z-speedrun-talent: invalid URL
- a16z-speedrun-talent: URL must use HTTPS
- agentic-jobs: invalid URL
- agentic-jobs: untrusted hostname
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/7f8a80765fc731ed.
Report an issue: GitHub.
Appendix: source
Thrown at providers/a16z-speedrun-talent.mjs:49
// feed's reported total_pages (or, when the feed omits it, a short page), so
// on an honest feed the cap costs nothing and full-board sweeps keep working
// as the board grows.
// It only bites a misbehaving feed or an absurd max_pages entry — so it
// sits well above plausible board size (~353 pages / ~17.6k jobs as of
// 2026-08), same policy as workday.mjs's cap.
const MAX_PAGES_CAP = 1000;
/** @param {string} url */
function assertFeedUrl(url) {
let parsed;
try {
parsed = new URL(url);
} catch {
throw new Error(`a16z-speedrun-talent: invalid URL: ${url}`);
}
if (parsed.protocol !== 'https:') throw new Error(`a16z-speedrun-talent: URL must use HTTPS: ${url}`);
if (parsed.hostname !== TRUSTED_HOST) {
throw new Error(`a16z-speedrun-talent: untrusted hostname "${parsed.hostname}" — must be ${TRUSTED_HOST}`);
}
return url;
}
/** Resolve the page cap: a positive integer `max_pages` on the entry, capped. */
function resolveMaxPages(entry) {
const v = entry?.max_pages;
if (Number.isInteger(v) && v > 0) return Math.min(v, MAX_PAGES_CAP);
return DEFAULT_MAX_PAGES;
}
/** Optional server-side query: `q:` on the entry, else joined `keywords:`. */
function resolveQuery(entry) {
if (typeof entry?.q === 'string' && entry.q.trim()) return entry.q.trim();
if (Array.isArray(entry?.keywords) && entry.keywords.length > 0) {
const joined = entry.keywords.filter((k) => typeof k === 'string' && k.trim()).join(' ').trim();
if (joined) return joined;
}View on GitHub (pinned to aac998c7ed)