santifer/career-ops · error · Error

${data.error.message}

Error message

${data.error.message}

What it means

Thrown when the completions endpoint returns HTTP 200 but the JSON body contains an `error` object — OpenRouter's way of reporting provider-level failures (upstream model errors, moderation, context-length overflow) with a 200 envelope. The library surfaces `data.error.message` verbatim so the OpenRouter-provided explanation is what the developer sees.

Solutions

  1. Read data.error.message in the thrown message — it identifies the upstream cause.
  2. If it is a context-length error, shorten the prompt or pick a model with a larger window.
  3. If the upstream model is failing, retry — the rotation should blacklist it and try another free model.
  4. If moderation-related, rephrase the prompt.
  5. Treat 200-with-error as a failure signal in any monitoring; don't count it as success because the HTTP status was OK.

Example fix

// before
if (data.error) throw new Error(data.error.message);
// after
if (data.error) {
  const err = new Error(data.error.message ?? JSON.stringify(data.error));
  err.code = data.error.code;
  err.upstream = true;   // rotation logic can blacklist this model
  throw err;
}
Defensive patterns

Strategy: fallback

Type guard

function hasUpstreamError(data) {
  return data != null && typeof data === 'object' && data.error != null;
}

Try / catch

try {
  result = await callOpenRouter(systemPrompt, userMessage);
} catch (err) {
  // rotation already blacklists the model; fall back to a known-good model
  result = await callWithFallbackModel(systemPrompt, userMessage);
}

Prevention

When it happens

Trigger: Response is 200 OK, resp.json() succeeds, and `data.error` is truthy — e.g. upstream provider timed out, the model refused/errored, prompt exceeded the model's context window, or content moderation rejected the request.

Common situations: Free-tier model's upstream provider returning errors under load; prompts longer than the model's context limit; provider deprecating a model but still accepting requests that fail downstream.

Related errors


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

Appendix: source

Thrown at openrouter-runner.mjs:262

    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/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(

View on GitHub (pinned to aac998c7ed)