{"record":{"id":"b2d8e222179d0c80","repo":"santifer/career-ops","slug":"wttj-unexpected-algolia-app-id-appid","errorCode":null,"errorMessage":"wttj: unexpected Algolia app id \"${appId}\"","messagePattern":"wttj: unexpected Algolia app id \"(.+?)\"","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/wttj.mjs","lineNumber":67,"sourceCode":" * Algolia application id + client search key.\n * @param {string} text\n * @returns {{ appId: string, apiKey: string }}\n */\nexport function parseEnvPayload(text) {\n  const start = text.indexOf('{');\n  const end = text.lastIndexOf('}');\n  if (start === -1 || end <= start) throw new Error('wttj: /api/env payload has no JSON object');\n  let env;\n  try {\n    env = JSON.parse(text.slice(start, end + 1));\n  } catch {\n    throw new Error('wttj: /api/env payload is not valid JSON');\n  }\n  const appId = typeof env.PUBLIC_ALGOLIA_APPLICATION_ID === 'string' ? env.PUBLIC_ALGOLIA_APPLICATION_ID.trim() : '';\n  const apiKey = typeof env.PUBLIC_ALGOLIA_API_KEY_CLIENT === 'string' ? env.PUBLIC_ALGOLIA_API_KEY_CLIENT.trim() : '';\n  // App ids are short alphanumerics; validating keeps the derived Algolia\n  // hostname from being attacker-shaped if the env payload ever changes.\n  if (!/^[A-Z0-9]{6,16}$/i.test(appId)) throw new Error(`wttj: unexpected Algolia app id \"${appId}\"`);\n  // The key is only ever sent as a request header (never used to build a\n  // host), so don't over-constrain its format — WTTJ may rotate to a longer\n  // or non-hex (e.g. secured/base64) client key. Length bounds only.\n  if (!apiKey || apiKey.length < 16 || apiKey.length > 500) {\n    throw new Error('wttj: unexpected Algolia api key shape');\n  }\n  return { appId, apiKey };\n}\n\n/**\n * Normalize a single Algolia hit. Exported for tests.\n *\n * Field mapping → normalized Job shape:\n *   - title:    `name`\n *   - url:      /en/companies/{organization.slug}/jobs/{slug} on the WTTJ site\n *   - company:  `organization.name`\n *   - location: offices[0] city+country, with \", Remote\" appended when the\n *               posting allows fulltime remote","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/santifer/career-ops/blob/9b17a8ac97b398a496b38e423ae24e433b43254f/providers/wttj.mjs#L49-L85","documentation":"The parsed PUBLIC_ALGOLIA_APPLICATION_ID must match ^[A-Z0-9]{6,16}$i. This validates the Algolia app id so the derived hostname (${appId}-dsn.algolia.net) cannot be attacker-shaped if /api/env is ever tampered. Throws when the id is missing, empty after trim, or a different shape.","triggerScenarios":"/api/env no longer contains PUBLIC_ALGOLIA_APPLICATION_ID; the field was renamed; WTTJ rotated to an app id format outside the 6–16 alphanumeric range; the env object parsed but the key is absent.","commonSituations":"WTTJ renamed the env key; WTTJ's Algolia app id changed length/format; a tampered env payload.","solutions":["Fetch /api/env and check the actual value of PUBLIC_ALGOLIA_APPLICATION_ID.","If WTTJ rotated to a validly-shaped id, no code change is needed (just rerun).","If the format genuinely changed (e.g. longer), widen the regex in parseEnvPayload after confirming the new shape against Algolia's docs.","If the key was renamed, update the two field reads in parseEnvPayload."],"exampleFix":"// if WTTJ rotates to a longer app id (e.g. 20 chars):\n// before\nif (!/^[A-Z0-9]{6,16}$/i.test(appId)) throw new Error(`wttj: unexpected Algolia app id \"${appId}\"`);\n// after\nif (!/^[A-Z0-9]{6,24}$/i.test(appId)) throw new Error(`wttj: unexpected Algolia app id \"${appId}\"`);","handlingStrategy":"validation","validationCode":"const APPID_RE = /^[A-Z0-9]{6,16}$/i;\nif (!APPID_RE.test(parsedAppId))\n  console.warn(\"wttj app id shape changed:\", parsedAppId);","typeGuard":"const isValidAlgoliaAppId = (s) => typeof s === \"string\" && /^[A-Z0-9]{6,16}$/i.test(s);","tryCatchPattern":"try { parseEnvPayload(envText); }\ncatch (err) {\n  if (/Algolia app id/.test(err.message)) { logUpstreamChange(\"wttj\", err.message); }\n  throw err;\n}","preventionTips":["Alert on this error — it signals either a tampered payload or a real WTTJ rotation.","Keep the regex tight; only widen it after confirming the new id shape.","Pin a captured /api/env fixture in CI to detect key renames."],"tags":["api-contract","wttj","algolia","validation","key-rotation"],"backgroundTag":null,"analyzedSha":"9b17a8ac97b398a496b38e423ae24e433b43254f","analyzedAt":"2026-08-13T00:48:39.135Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}