santifer/career-ops · error · Error
Timeout after ${MODEL_TIMEOUT_MS / 1000}s
Error message
Timeout after ${MODEL_TIMEOUT_MS / 1000}s What it means
Thrown inside the rotation loop of callOpenRouter() when the AbortController fires because a rotated model's request exceeded MODEL_TIMEOUT_MS (15 seconds). The catch converts AbortError into this timeout message. Like error 75, this is caught by the loop and feeds retry logic — it propagates only if every model ultimately fails.
Source
Thrown at openrouter-runner.mjs:301
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: controller.signal,
});
if (!resp.ok) {
const t = await resp.text();
throw new Error(`HTTP ${resp.status}: ${t.slice(0, 120)}`);
}
data = await resp.json();
} catch (e) {
if (e.name === 'AbortError') throw new Error(`Timeout after ${MODEL_TIMEOUT_MS / 1000}s`);
throw e;
} finally {
clearTimeout(timerId);
}
if (data.error) throw new Error(data.error.message);
const content = data.choices?.[0]?.message?.content ?? '';
if (!content) throw new Error('Empty response');
const usage = normalizeOpenAIUsage(data.usage);
modelIndex = (modelIndex + attempt + 1) % active.length;
console.log('OK');
return { content, usage };
} catch (e) {
lastError = e;
const msg = e.message.split('\n')[0];View on GitHub (pinned to 9b17a8ac97)
Solutions
- Let rotation continue — the loop will try the next, faster model.
- Reduce prompt size to lower per-model latency.
- Pin a known-fast free model via CAREER_OPS_MODEL.
- If timeouts hit every model, suspect the network path to openrouter.ai.
Defensive patterns
Strategy: retry
Try / catch
// Rotation handles timeouts internally. Only act if all models time out (error 78). // For a single-model timeout, reduce prompt size or pin a faster model.
Prevention
- Prefer rotation so a single slow model is skipped automatically.
- Trim prompt size to reduce per-model latency.
- Pin a known-fast free model for latency-sensitive runs.
- Investigate the network path if every model times out.
When it happens
Trigger: During rotation, a model's fetch did not complete within 15000ms and the abort signal fired. Happens with slow large models, network congestion, or an overloaded provider for that specific model.
Common situations: Rotating onto a slow/large free model; network latency spike; a particular provider is slow under load; large prompt inflating time-to-first-token.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Pinned model timed out after ${MODEL_TIMEOUT_MS / 1000}s
- All ${active.length} active models failed. Last error: ${las
- [models] Failed to fetch free model list: ${reason}. Check t
- OPENROUTER_API_KEY not found. Copy .env.example to .env and
- HTTP ${resp.status}: ${t.slice(0, 120)}
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/fdf0f0964e31b008.
Report an issue: GitHub.