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

  1. Let rotation continue — the loop will try the next, faster model.
  2. Reduce prompt size to lower per-model latency.
  3. Pin a known-fast free model via CAREER_OPS_MODEL.
  4. 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

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

Related errors


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