santifer/career-ops · warning · Error

Pinned model timed out after

Error message

Pinned model timed out after ${MODEL_TIMEOUT_MS / 1000}s

What it means

Rewritten inside callOpenRouter()'s catch: any AbortError from the fetch's AbortController (the MODEL_TIMEOUT_MS deadline firing) is converted into this human-readable timeout message naming the configured limit in seconds. It means the pinned/selected model did not return within the per-request timeout.

Solutions

  1. Retry — the rotation should blacklist the slow model and try the next one.
  2. Increase MODEL_TIMEOUT_MS if your prompts legitimately need more time.
  3. Prefer faster models in PROVIDER_PRIORITY or pin a model known to be responsive.
  4. Check the provider/OpenRouter status on correlated timeouts.
  5. Reduce prompt size to cut generation latency.

Example fix

// before
signal: ctrl.signal,
// after (per-attempt timeout scaled to prompt size)
const timeoutMs = Math.max(MODEL_TIMEOUT_MS, estimateTokens(body) * 50);
signal: AbortSignal.timeout(timeoutMs),
Defensive patterns

Strategy: retry

Validate before calling

const estimatedMs = Math.ceil(promptChars / 3) * 60; // rough latency budget
if (estimatedMs > MODEL_TIMEOUT_MS) console.warn(`Prompt may exceed the ${MODEL_TIMEOUT_MS}ms model timeout`);

Try / catch

try {
  result = await callOpenRouter(systemPrompt, userMessage);
} catch (err) {
  if (/timed out after/.test(err.message) && attempt < MAX_ATTEMPTS) {
    attempt++; // rotation picks a faster model on the next try
    return callWithRotation(systemPrompt, userMessage, attempt);
  }
  throw err;
}

Prevention

When it happens

Trigger: A slow free model takes longer than MODEL_TIMEOUT_MS to stream/complete its response, or the connection hangs — the abort signal cancels the fetch and the AbortError is translated here.

Common situations: Large prompts against a congested free-tier model; provider slowness or partial outage; MODEL_TIMEOUT_MS set too low for the workload; network stall without RST keeping the request hanging.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16). Data as JSON: /api/errors/85c596265565de25. Report an issue: GitHub.

Appendix: source

Thrown at openrouter-runner.mjs:269

          'HTTP-Referer':  'https://github.com/career-ops-hq/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;

  if (models.length === 0) {
    throw new Error(
      'No free OpenRouter models are available. Model loading may have failed, or your account currently has no free models.'
    );
  }
  // Build the active (non-blacklisted) model list in rotation order
  const active = models.filter(m => !blacklistedModels.has(m));
  if (active.length === 0) throw new Error('All loaded models have been blacklisted this session.');

View on GitHub (pinned to aac998c7ed)