santifer/career-ops · error · Error
wttj: unexpected Algolia api key shape
Error message
wttj: unexpected Algolia api key shape
What it means
parseEnvPayload validates the Algolia client API key from WTTJ's /api/env payload with only length bounds (non-empty, 16-500 chars). The key is only sent as a request header, never used to build a hostname, so the format is deliberately not over-constrained — but an absent, empty, or absurdly short/long key still fails this guard. It indicates the env payload did not contain a usable PUBLIC_ALGOLIA_API_KEY_CLIENT value.
Solutions
- Re-fetch the /api/env payload manually and confirm PUBLIC_ALGOLIA_API_KEY_CLIENT is present and its actual length
- If the field was renamed/removed, update the property name in parseEnvPayload
- Check for proxies/CDN caches returning stale or sanitized responses; bypass or purge them
- If WTTJ rotated to a shorter key format, adjust the >=16 lower bound after verifying the real key shape
Example fix
// before
if (!apiKey || apiKey.length < 16 || apiKey.length > 500) {
throw new Error('wttj: unexpected Algolia api key shape');
}
// after (accept rotated shorter key, verified from live payload)
if (!apiKey || apiKey.length < 12 || apiKey.length > 500) {
throw new Error('wttj: unexpected Algolia api key shape');
} Defensive patterns
Strategy: validation
Validate before calling
const env = JSON.parse(envText);
const key = env?.PUBLIC_ALGOLIA_API_KEY_CLIENT;
if (typeof key !== 'string' || key.trim().length < 16 || key.trim().length > 500) {
throw new Error('env payload lacks a usable Algolia client key — check /api/env output');
} Type guard
function hasAlgoliaClientKey(env) {
const k = env?.PUBLIC_ALGOLIA_API_KEY_CLIENT;
return typeof k === 'string' && k.trim().length >= 16 && k.trim().length <= 500;
} Try / catch
try {
await scanWttj(entry);
} catch (e) {
if (e.message === 'wttj: unexpected Algolia api key shape') {
console.warn('PUBLIC_ALGOLIA_API_KEY_CLIENT missing or out of bounds — WTTJ may have rotated it.');
} else throw e;
} Prevention
- Treat the key as opaque: only length checks, never format assumptions (it may become base64/secured)
- Diff a recorded /api/env fixture against live output when the key check fails
- Ensure HTTP caches/proxies don't strip response fields; test from a clean network
- Keep the error message distinct from the app-id error so triage is immediate
When it happens
Trigger: The /api/env response lacks PUBLIC_ALGOLIA_API_KEY_CLIENT (field not a string, or empty/whitespace after trim), or the string is shorter than 16 chars or longer than 500 chars.
Common situations: WTTJ rotates or renames the client key field; an anti-bot challenge page or cached empty body is returned instead of the env JSON; a proxy strips the field; the key was truncated by an intermediary.
Understand the failure class
Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.
Related errors
- wttj: unexpected Algolia app id
- 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/62ee3dae4fa106ca.
Report an issue: GitHub.
Appendix: source
Thrown at providers/wttj.mjs:97
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 remote
* - postedAt: `published_at_timestamp` (epoch seconds → ms)
* - salary: {min, max, currency} from salary_yearly_minimum/salary_maximum
*
* @param {any} h
* @returns {{ title: string, url: string, company: string, location: string, postedAt?: number, salary?: {min: number, max: number, currency: string} } | null}View on GitHub (pinned to aac998c7ed)