santifer/career-ops · error · Error

wttj: unexpected Algolia response for query "${query}" — exp

Error message

wttj: unexpected Algolia response for query "${query}" — expected { hits: [...] }, got keys: [${json ? Object.keys(json).join(', ') : 'null'}]

What it means

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.

Source

Thrown at providers/wttj.mjs:200

        attributesToRetrieve:
          'name,slug,organization,offices,remote,published_at_timestamp,salary_yearly_minimum,salary_maximum,salary_period,salary_currency',
      });
      const json = /** @type {any} */ (
        await ctx.fetchJson(url, {
          method: 'POST',
          redirect: 'error',
          headers: {
            'x-algolia-application-id': appId,
            'x-algolia-api-key': apiKey,
            // The client search key is referer-locked to the WTTJ site.
            referer: `${SITE_ORIGIN}/`,
            'content-type': 'application/json',
          },
          body: JSON.stringify({ params: params.toString() }),
        })
      );
      if (!json || !Array.isArray(json.hits)) {
        throw new Error(
          `wttj: unexpected Algolia response for query "${query}" — expected { hits: [...] }, got keys: [${json ? Object.keys(json).join(', ') : 'null'}]`,
        );
      }
      for (const h of json.hits) {
        const job = normalizeWttjHit(h);
        if (job && !byUrl.has(job.url)) byUrl.set(job.url, job);
      }
    }
    return [...byUrl.values()];
  },
};

View on GitHub (pinned to 9b17a8ac97)

Solutions

  1. Inspect the reported keys in the message — { message, status } indicates an Algolia auth/error response; act on its status.
  2. Re-derive fresh credentials by re-fetching /api/env (parseEnvPayload) in case the key rotated.
  3. Verify the Referer header is being sent as https://www.welcometothejungle.com/ (the key is referer-locked).
  4. If the index was renamed, update the INDEX constant.
  5. Retry after a backoff if it is a rate limit.

Example fix

// before — treat any non-hits as fatal with raw keys
if (!json || !Array.isArray(json.hits)) {
  throw new Error(`wttj: unexpected Algolia response for query "${query}" — got keys: [${json ? Object.keys(json).join(", ") : "null"}]`);
}
// after — surface Algolia's own error message for diagnosis
if (!json || !Array.isArray(json.hits)) {
  const alg = json?.message ? ` (Algolia: ${json.message}, status ${json.status ?? "?"})` : "";
  throw new Error(`wttj: unexpected Algolia response for query "${query}"${alg}`);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// probe credentials + index before the scan
const probe = await ctx.fetchJson(
  `https://${appId}-dsn.algolia.net/1/indexes/${INDEX}/query`,
  { method: "POST",
    headers: { "x-algolia-application-id": appId, "x-algolia-api-key": apiKey,
               referer: SITE_ORIGIN + "/" },
    body: JSON.stringify({ params: new URLSearchParams({ query: "test", hitsPerPage: "1" }).toString() }) }
);
if (!Array.isArray(probe?.hits)) console.warn("wttj algolia not returning hits — check key/index");

Type guard

const isAlgoliaHitsResponse = (j) => !!j && Array.isArray(j.hits);

Try / catch

try {
  for (const query of queries) { /* Algolia POST */ }
} catch (err) {
  if (/unexpected Algolia response/.test(err.message)) {
    logAuthOrContractIssue("wttj", err.message);
    continue;
  }
  throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13). Data as JSON: /api/errors/74df7ae66299f8e4. Report an issue: GitHub.