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
- 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.
- Unset CAREER_OPS_MODEL to fall back to automatic free-model rotation which skips failing models.
- Verify the exact model id spelling (e.g. 'google/gemini-2.0-flash-exp:free').
- 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 pinning CAREER_OPS_MODEL, copy the exact id from https://openrouter.ai/models (note the :free suffix).
- Prefer rotation over pinning unless you need a specific model — rotation skips failing models.
- Handle 401/404/429 distinctly: fix key, fix model id, or back off.
- Log the status+body snippet so the underlying OpenRouter error is visible.
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
- [models] Failed to fetch free model list: ${reason}. Check t
- OPENROUTER_API_KEY not found. Copy .env.example to .env and
- ${data.error.message}
- Empty response
- Pinned model timed out after ${MODEL_TIMEOUT_MS / 1000}s
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/88d829cdf9e72fb0.
Report an issue: GitHub.