{"record":{"id":"74df7ae66299f8e4","repo":"santifer/career-ops","slug":"wttj-unexpected-algolia-response-for-query-que","errorCode":null,"errorMessage":"wttj: unexpected Algolia response for query \"${query}\" — expected { hits: [...] }, got keys: [${json ? Object.keys(json).join(', ') : 'null'}]","messagePattern":"wttj: unexpected Algolia response for query \"(.+?)\" — expected (.+?), got keys: \\[(.+?)\\]","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/wttj.mjs","lineNumber":200,"sourceCode":"        attributesToRetrieve:\n          'name,slug,organization,offices,remote,published_at_timestamp,salary_yearly_minimum,salary_maximum,salary_period,salary_currency',\n      });\n      const json = /** @type {any} */ (\n        await ctx.fetchJson(url, {\n          method: 'POST',\n          redirect: 'error',\n          headers: {\n            'x-algolia-application-id': appId,\n            'x-algolia-api-key': apiKey,\n            // The client search key is referer-locked to the WTTJ site.\n            referer: `${SITE_ORIGIN}/`,\n            'content-type': 'application/json',\n          },\n          body: JSON.stringify({ params: params.toString() }),\n        })\n      );\n      if (!json || !Array.isArray(json.hits)) {\n        throw new Error(\n          `wttj: unexpected Algolia response for query \"${query}\" — expected { hits: [...] }, got keys: [${json ? Object.keys(json).join(', ') : 'null'}]`,\n        );\n      }\n      for (const h of json.hits) {\n        const job = normalizeWttjHit(h);\n        if (job && !byUrl.has(job.url)) byUrl.set(job.url, job);\n      }\n    }\n    return [...byUrl.values()];\n  },\n};\n","sourceCodeStart":182,"sourceCodeEnd":212,"githubUrl":"https://github.com/santifer/career-ops/blob/9b17a8ac97b398a496b38e423ae24e433b43254f/providers/wttj.mjs#L182-L212","documentation":"For each configured query the provider POSTs to ${appId}-dsn.algolia.net/1/indexes/wttj_jobs_production_en/query and expects a response object with a hits array. This throws when the response is null/falsy or hits is not an array — i.e. Algolia returned an error envelope instead of results.","triggerScenarios":"Algolia returned { message, status } (auth failure, invalid key, index missing); the referer-locked key was rejected because the Referer header was stripped; rate limit / quota exceeded; the index wttj_jobs_production_en was renamed; a network proxy returned a non-Algolia JSON body.","commonSituations":"The client search key expired or the referer lock no longer matches WTTJ's configured allowed referer; WTTJ renamed the production index; Algolia rate-limiting the scanner; a transient 4xx mapped to a JSON error body.","solutions":["Inspect the reported keys in the message — { message, status } indicates an Algolia auth/error response; act on its status.","Re-derive fresh credentials by re-fetching /api/env (parseEnvPayload) in case the key rotated.","Verify the Referer header is being sent as https://www.welcometothejungle.com/ (the key is referer-locked).","If the index was renamed, update the INDEX constant.","Retry after a backoff if it is a rate limit."],"exampleFix":"// before — treat any non-hits as fatal with raw keys\nif (!json || !Array.isArray(json.hits)) {\n  throw new Error(`wttj: unexpected Algolia response for query \"${query}\" — got keys: [${json ? Object.keys(json).join(\", \") : \"null\"}]`);\n}\n// after — surface Algolia's own error message for diagnosis\nif (!json || !Array.isArray(json.hits)) {\n  const alg = json?.message ? ` (Algolia: ${json.message}, status ${json.status ?? \"?\"})` : \"\";\n  throw new Error(`wttj: unexpected Algolia response for query \"${query}\"${alg}`);\n}","handlingStrategy":"try-catch","validationCode":"// probe credentials + index before the scan\nconst probe = await ctx.fetchJson(\n  `https://${appId}-dsn.algolia.net/1/indexes/${INDEX}/query`,\n  { method: \"POST\",\n    headers: { \"x-algolia-application-id\": appId, \"x-algolia-api-key\": apiKey,\n               referer: SITE_ORIGIN + \"/\" },\n    body: JSON.stringify({ params: new URLSearchParams({ query: \"test\", hitsPerPage: \"1\" }).toString() }) }\n);\nif (!Array.isArray(probe?.hits)) console.warn(\"wttj algolia not returning hits — check key/index\");","typeGuard":"const isAlgoliaHitsResponse = (j) => !!j && Array.isArray(j.hits);","tryCatchPattern":"try {\n  for (const query of queries) { /* Algolia POST */ }\n} catch (err) {\n  if (/unexpected Algolia response/.test(err.message)) {\n    logAuthOrContractIssue(\"wttj\", err.message);\n    continue;\n  }\n  throw err;\n}","preventionTips":["Always send the WTTJ Referer header — the client key is referer-locked.","Re-derive credentials each run (already done via /api/env) so a rotated key self-heals.","Alert on this error to catch index renames or key-rotation failures early."],"tags":["api-contract","wttj","algolia","auth","validation"],"backgroundTag":null,"analyzedSha":"9b17a8ac97b398a496b38e423ae24e433b43254f","analyzedAt":"2026-08-13T00:48:39.135Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}