santifer/career-ops · error · Error
wttj: untrusted hostname " " — must be
Error message
wttj: untrusted ${label} hostname "${parsed.hostname}" — must be ${host} What it means
assertHost's final check compares parsed.hostname (case-insensitively) against the exact expected host parameter. Any other hostname — a lookalike domain, a redirect target, or a typo — is rejected to keep fetches pinned to the legitimate WTTJ/ATS host.
Solutions
- Correct the URL to use the expected host named in the error message
- Check for typos or appended domains in the hostname
- If you passed the wrong host constant to assertHost, fix the call site
Example fix
// before
assertHost('https://jobs.welcome-to-the-jungle.com/...', 'www.wttj.fr', 'env');
// after
assertHost('https://www.wttj.fr/api/env', 'www.wttj.fr', 'env'); Defensive patterns
Strategy: validation
Validate before calling
function hostIs(u, expected) { try { return new URL(u).hostname === expected.toLowerCase(); } catch { return false; } }
if (!hostIs(entry.careers_url, 'www.wttj.fr')) throw new Error(`unexpected host in ${entry.careers_url}`); Type guard
const hostIs = (u, expected) => { try { return new URL(u).hostname === expected.toLowerCase(); } catch { return false; } }; Try / catch
try { return await wttj.fetch(entry, ctx); } catch (e) { if (e.message.includes('untrusted')) { console.warn(`${entry.name}: hostname not pinned to ${e.message.match(/must be (\S+)/)?.[1]}`); return []; } throw e; } Prevention
- Compare hostnames (not hrefs) when validating configured URLs
- Beware lookalike domains when copying careers URLs
- Keep the expected host constants next to the assertHost call sites and review changes
When it happens
Trigger: assertHost(url, host, label) where url parses to https but hostname differs from host, e.g. assertHost('https://evil.io/api/env', 'www.wttj.fr', 'env') or 'https://www.wttj.fr.evil.io/api/env'.
Common situations: A careers_url pointing at the company site rather than the WTTJ board domain; an attacker-shaped host in config; passing the wrong host argument order to assertHost.
Related errors
- workable: untrusted hostname
- collage: untrusted hostname
- flowxtra: URL must use HTTPS
- senjob: untrusted hostname
- smartrecruiters: untrusted hostname
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/f6873f68ae0d3dde.
Report an issue: GitHub.
Appendix: source
Thrown at providers/wttj.mjs:67
// manager" alone returns ~14k hits — so Algolia's own relevance ranking, not the
// scanner's filters, decides which 200 are seen. A server-side `filters`
// expression cuts the result set to something a single request can actually
// exhaust (e.g. product-management + France + full_time is ~450), so the cap is
// raised to Algolia's per-request ceiling for this index when one is configured.
const FILTERED_MAX_HITS_CAP = 1000;
const FILTERS_MAX_LEN = 1000;
/** Pin a URL to an expected https host. */
function assertHost(url, host, label) {
let parsed;
try {
parsed = new URL(url);
} catch {
throw new Error(`wttj: invalid URL: ${url}`);
}
if (parsed.protocol !== 'https:') throw new Error(`wttj: URL must use HTTPS: ${url}`);
if (parsed.hostname !== host.toLowerCase()) {
throw new Error(`wttj: untrusted ${label} hostname "${parsed.hostname}" — must be ${host}`);
}
return url;
}
/**
* Parse the `window.env = {...}` payload served by /api/env and extract the
* Algolia application id + client search key.
* @param {string} text
* @returns {{ appId: string, apiKey: string }}
*/
export function parseEnvPayload(text) {
const start = text.indexOf('{');
const end = text.lastIndexOf('}');
if (start === -1 || end <= start) throw new Error('wttj: /api/env payload has no JSON object');
let env;
try {
env = JSON.parse(text.slice(start, end + 1));
} catch {View on GitHub (pinned to aac998c7ed)