santifer/career-ops · error · Error
wttj: unexpected Algolia app id
Error message
wttj: unexpected Algolia app id "${appId}" What it means
parseEnvPayload extracts the Algolia application id from WTTJ's /api/env payload and validates it against /^[A-Z0-9]{6,16}$/i before using it to derive the Algolia hostname. If PUBLIC_ALGOLIA_APPLICATION_ID is missing, empty, or shaped differently than expected, this error is thrown to prevent building an attacker-shaped hostname from an untrusted value. The app id is the interpolation in the message, so it shows exactly what was received.
Solutions
- Fetch https://www.welcometothejungle.com and inspect the /api/env response to confirm PUBLIC_ALGOLIA_APPLICATION_ID exists and note its exact value
- If the field was renamed, update the property name in parseEnvPayload to match the new payload key
- Relax or update the /^[A-Z0-9]{6,16}$/i regex only if the new id format is verified legitimate, keeping the length bound to avoid attacker-shaped hosts
- Clear any caching layer (CDN, HTTP cache) that may have stored a stale or error response for /api/env
Example fix
// before: WTTJ renamed the field const appId = typeof env.PUBLIC_ALGOLIA_APPLICATION_ID === 'string' ? env.PUBLIC_ALGOLIA_APPLICATION_ID.trim() : ''; // after const appId = typeof (env.PUBLIC_ALGOLIA_APPLICATION_ID ?? env.PUBLIC_ALGOLIA_APP_ID) === 'string' ? (env.PUBLIC_ALGOLIA_APPLICATION_ID ?? env.PUBLIC_ALGOLIA_APP_ID).trim() : '';
Defensive patterns
Strategy: validation
Validate before calling
// before calling the provider, sanity-check the env payload yourself
const env = JSON.parse(envText);
const appId = env?.PUBLIC_ALGOLIA_APPLICATION_ID;
if (typeof appId !== 'string' || !/^[A-Z0-9]{6,16}$/i.test(appId.trim())) {
throw new Error('env payload lacks a usable Algolia app id — WTTJ page changed?');
} Type guard
function hasAlgoliaAppId(env) {
return typeof env?.PUBLIC_ALGOLIA_APPLICATION_ID === 'string' &&
/^[A-Z0-9]{6,16}$/.test(env.PUBLIC_ALGOLIA_APPLICATION_ID.trim());
} Try / catch
try {
await scanWttj(entry);
} catch (e) {
if (e.message.startsWith('wttj: unexpected Algolia app id')) {
console.warn('WTTJ env payload changed or was intercepted — inspect /api/env manually.');
} else throw e;
} Prevention
- Check the /api/env payload shape after any WTTJ scraping failure before changing code
- Never widen the appId regex beyond alphanumerics — the id is used to build a hostname
- Cache the env payload briefly but revalidate on repeated failures
- Pin tests that parse a recorded env payload fixture to catch field renames early
When it happens
Trigger: The fetched /api/env payload has no PUBLIC_ALGOLIA_APPLICATION_ID field (property renamed or removed), the field is not a string, it is empty/whitespace after trim, or its value is not 6-16 alphanumeric characters (e.g. truncated, HTML error page captured, or WTTJ rotated to a different id format).
Common situations: WTTJ changes their public env payload field names; a proxy or anti-bot page returns HTML instead of the JSON env payload; the scraper hits a regional/changed endpoint; network middleware rewrites the response.
Understand the failure class
Background: "is not a valid" / "Invalid ... value" environment variable errors: how libraries validate env vars and what to do when they reject yours — this error's family across 48 libraries.
Related errors
- wttj: unexpected Algolia api key shape
- wttj: `filters` is too long
- wttj: unexpected Algolia response for query
- yourator: invalid URL
- 4dayweek: invalid URL
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/b2d8e222179d0c80.
Report an issue: GitHub.
Appendix: source
Thrown at providers/wttj.mjs:92
* 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 {
throw new Error('wttj: /api/env payload is not valid JSON');
}
const appId = typeof env.PUBLIC_ALGOLIA_APPLICATION_ID === 'string' ? env.PUBLIC_ALGOLIA_APPLICATION_ID.trim() : '';
const apiKey = typeof env.PUBLIC_ALGOLIA_API_KEY_CLIENT === 'string' ? env.PUBLIC_ALGOLIA_API_KEY_CLIENT.trim() : '';
// App ids are short alphanumerics; validating keeps the derived Algolia
// hostname from being attacker-shaped if the env payload ever changes.
if (!/^[A-Z0-9]{6,16}$/i.test(appId)) throw new Error(`wttj: unexpected Algolia app id "${appId}"`);
// The key is only ever sent as a request header (never used to build a
// host), so don't over-constrain its format — WTTJ may rotate to a longer
// or non-hex (e.g. secured/base64) client key. Length bounds only.
if (!apiKey || apiKey.length < 16 || apiKey.length > 500) {
throw new Error('wttj: unexpected Algolia api key shape');
}
return { appId, apiKey };
}
/**
* Normalize a single Algolia hit. Exported for tests.
*
* Field mapping → normalized Job shape:
* - title: `name`
* - url: /en/companies/{organization.slug}/jobs/{slug} on the WTTJ site
* - company: `organization.name`
* - location: offices[0] city+country, with ", Remote" appended when the
* posting allows fulltime remoteView on GitHub (pinned to aac998c7ed)