santifer/career-ops · error · Error

wttj: /api/env payload has no JSON object

Error message

wttj: /api/env payload has no JSON object

What it means

parseEnvPayload extracts the substring between the first { and the last } of the /api/env response (the window.env = {...} block). This throws when there is no { at all, or no } after it — i.e. the response contains no JSON-object-looking span.

Source

Thrown at providers/wttj.mjs:56

    throw new Error(`wttj: invalid URL: ${url}`);
  }
  if (parsed.protocol !== 'https:') throw new Error(`wttj: URL must use HTTPS: ${url}`);
  if (parsed.hostname !== host.toLowerCase()) {
    throw new Error(`wttj: untrusted ${label} hostname "${parsed.hostname}" — must be ${host}`);
  }
  return url;
}

/**
 * Parse the `window.env = {...}` payload served by /api/env and extract the
 * 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 };

View on GitHub (pinned to 9b17a8ac97)

Solutions

  1. Fetch https://www.welcometothejungle.com/api/env in a browser and confirm it still contains a window.env = {...} block.
  2. If it is an error page, retry later.
  3. If WTTJ changed the bootstrap, update parseEnvPayload's extraction to match (scope to the window.env assignment).
  4. Check the HTTP status — a non-200 may be masked by a 200 HTML page.

Example fix

// before — relies on literal window.env braces anywhere in the page
const start = text.indexOf("{");
const end = text.lastIndexOf("}");
if (start === -1 || end <= start) throw new Error("wttj: /api/env payload has no JSON object");
// after — find the window.env assignment explicitly
const m = text.match(/window\.env\s*=\s*(\{[\s\S]*\})\s*;?\s*<\/?script/i);
if (!m) throw new Error("wttj: /api/env payload has no JSON object");
let env;
try { env = JSON.parse(m[1]); }
catch { throw new Error("wttj: /api/env payload is not valid JSON"); }
Defensive patterns

Strategy: try-catch

Validate before calling

const text = await fetch(ENV_URL).then(r => r.text());
if (text.indexOf("{") === -1 || text.lastIndexOf("}") <= text.indexOf("{")) {
  console.warn("wttj /api/env has no JSON object — possible maintenance page");
}

Type guard

const hasJsonObject = (t) => typeof t === "string" &&
  t.indexOf("{") !== -1 && t.lastIndexOf("}") > t.indexOf("{");

Try / catch

let creds;
try {
  creds = parseEnvPayload(envText);
} catch (err) {
  if (/\/api\/env payload/.test(err.message)) { logUpstreamChange("wttj", err.message); }
  throw err;
}

Prevention

When it happens

Trigger: /api/env returned an HTML error page, an empty body, plain text, or a payload whose braces are in the wrong order. Most often a transient error page from WTTJ/CDN, or a permanent markup change at WTTJ (no more literal window.env = {...}).

Common situations: WTTJ is returning a 200 HTML error/maintenance page; a CDN interstitial; WTTJ refactored the env bootstrap so it is no longer a literal window.env assignment.

Related errors


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