santifer/career-ops · error · Error

HTTP ${resp.status}: ${t.slice(0, 120)}

Error message

HTTP ${resp.status}: ${t.slice(0, 120)}

What it means

Thrown inside the pinned-model branch of callOpenRouter() (openrouter-runner.mjs) when the OpenRouter POST returns a non-2xx status. The message includes the HTTP status and the first 120 chars of the response body so the caller can see OpenRouter's error detail. This is the pinned-model (CAREER_OPS_MODEL) path — it does not auto-rotate, so a non-OK response surfaces directly.

Source

Thrown at openrouter-runner.mjs:236

      max_tokens: MAX_TOKENS,
    });
    const ctrl = new AbortController();
    const timerId = setTimeout(() => ctrl.abort(), MODEL_TIMEOUT_MS);
    try {
      const resp = await fetch(OPENROUTER_API_URL, {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${key}`,
          'Content-Type':  'application/json',
          'HTTP-Referer':  'https://github.com/santifer/career-ops',
          'X-Title':       'career-ops',
        },
        body,
        signal: ctrl.signal,
      });
      if (!resp.ok) {
        const t = await resp.text();
        throw new Error(`HTTP ${resp.status}: ${t.slice(0, 120)}`);
      }
      const data = await resp.json();
      if (data.error) throw new Error(data.error.message);
      const content = data.choices?.[0]?.message?.content ?? '';
      if (!content) throw new Error('Empty response');
      console.log('OK');
      const usage = normalizeOpenAIUsage(data.usage);
      return { content, usage };
    } catch (e) {
      if (e.name === 'AbortError') throw new Error(`Pinned model timed out after ${MODEL_TIMEOUT_MS / 1000}s`);
      throw e;
    } finally {
      clearTimeout(timerId);
    }
  }

  const models = await loadFreeModels();
  let lastError;

View on GitHub (pinned to 9b17a8ac97)

Solutions

  1. Read the status: 401 → fix the key; 404 → the pinned model id is wrong/deprecated, check https://openrouter.ai/models; 429 → wait or unpin to let rotation pick another model; 402 → the pinned model isn't free, pick a free one.
  2. Unset CAREER_OPS_MODEL to fall back to automatic free-model rotation which skips failing models.
  3. Verify the exact model id spelling (e.g. 'google/gemini-2.0-flash-exp:free').
  4. Retry after a short delay for transient 5xx.

Example fix

# before
export CAREER_OPS_MODEL=some/deprecated-model
# throws HTTP 404: ...

# after — let rotation choose, or pin a known-free model
unset CAREER_OPS_MODEL
# or
export CAREER_OPS_MODEL=google/gemini-2.0-flash-exp:free
Defensive patterns

Strategy: try-catch

Try / catch

try {
  return await callOpenRouter(systemPrompt, userMessage); // pinned path
} catch (e) {
  if (e.message.startsWith('HTTP ') && process.env.CAREER_OPS_MODEL) {
    // pinned model failed — fall back to automatic rotation
    delete process.env.CAREER_OPS_MODEL;
    return await callOpenRouter(systemPrompt, userMessage);
  }
  throw e;
}

Prevention

When it happens

Trigger: CAREER_OPS_MODEL is set, the request reaches OpenRouter, but resp.ok is false: 400 (bad request/unsupported model), 401 (bad key), 402 (payment required), 404 (unknown model id), 429 (rate limit), or 5xx (OpenRouter upstream error).

Common situations: Pinned a model id that doesn't exist or was deprecated (404); free-tier quota hit (402/429); key revoked (401); model temporarily unavailable on OpenRouter's side (5xx).

Related errors


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