thedotmack/claude-mem · error · ServerClassifiedProviderError

OpenRouter API error

Error message

OpenRouter API error: ${response.status} - ${bodyText}

What it means

OpenRouter returned a non-2xx HTTP status to the chat-completions request. The provider reads the body and throws classifyHttpProviderError with status, bodyText, headers, cause `OpenRouter API error: <status> - <body>`, and label 'OpenRouter'. The classifier maps quota bodies, 429 (with Retry-After), and 5xx to retryable kinds and other 4xx to unrecoverable.

Solutions

  1. Read the classified kind: retryable → retry with retryAfterMs; unrecoverable → fix credentials/model/payload.
  2. Top up OpenRouter credits if status is 402.
  3. Verify OPENROUTER_API_KEY is set and valid; check the model slug exists on openrouter.ai/models.
  4. Honor Retry-After on 429 and serialize/throttle parallel generation requests.

Example fix

// before
model = 'anthropic/claude-v2';
// after
model = 'anthropic/claude-sonnet-4'; // valid OpenRouter slug
if (!process.env.OPENROUTER_API_KEY) throw new Error('OPENROUTER_API_KEY missing');
Defensive patterns

Strategy: retry

Validate before calling

if (!process.env.OPENROUTER_API_KEY) throw new Error('OPENROUTER_API_KEY not set');
// validate the model slug is available: GET https://openrouter.ai/api/v1/models

Type guard

function isRetryableProviderError(e: unknown): boolean {
  return typeof e === 'object' && e !== null && 'kind' in e && (e as {kind: string}).kind === 'retryable';
}

Try / catch

try {
  result = await openRouterProvider.generate(prompt, signal);
} catch (e) {
  if (e.status === 402) throw new Error('OpenRouter credits exhausted — top up at openrouter.ai');
  if (e.kind === 'retryable' && e.retryAfterMs) return sleep(e.retryAfterMs).then(call);
  throw e;
}

Prevention

When it happens

Trigger: `response.ok` is false after postChatCompletion: 401 missing/invalid OPENROUTER_API_KEY, 402 insufficient credits, 429 rate limit, 400 bad model slug, 5xx upstream model errors — thrown at OpenRouterObservationProvider.ts:93-100.

Common situations: Out of OpenRouter credits (402), revoked or missing API key, invalid model slug (model not found on OpenRouter), rate limiting from parallel requests, upstream provider outages surfaced as 502/503.

Related errors


AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17). Data as JSON: /api/errors/addcb9d58aebfc0a. Report an issue: GitHub.

Appendix: source

Thrown at src/server/generation/providers/OpenRouterObservationProvider.ts:96

        providerLabel: this.providerLabel,
        modelId: this.model,
      };
    }

    let response: Response;
    try {
      response = await this.postChatCompletion(prompt, signal);
    } catch (networkError) {
      const err = networkError instanceof Error ? networkError : new Error(String(networkError));
      throw classifyHttpProviderError({
        cause: err,
        providerLabel: 'OpenRouter',
      });
    }

    if (!response.ok) {
      const bodyText = await safeReadBody(response);
      throw classifyHttpProviderError({
        status: response.status,
        bodyText,
        headers: response.headers,
        cause: new Error(`OpenRouter API error: ${response.status} - ${bodyText}`),
        providerLabel: 'OpenRouter',
      });
    }

    let data: OpenRouterResponse;
    try {
      data = (await response.json()) as OpenRouterResponse;
    } catch (parseError) {
      const err = parseError instanceof Error ? parseError : new Error(String(parseError));
      throw new ServerClassifiedProviderError('OpenRouter returned invalid JSON', {
        kind: 'parse_error',
        cause: err,
      });
    }

View on GitHub (pinned to d8bc9755e7)