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
- Read the classified kind: retryable → retry with retryAfterMs; unrecoverable → fix credentials/model/payload.
- Top up OpenRouter credits if status is 402.
- Verify OPENROUTER_API_KEY is set and valid; check the model slug exists on openrouter.ai/models.
- 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
- Monitor credit balance; alert before 402s occur.
- Validate model slugs against the OpenRouter models list.
- Throttle concurrent requests to avoid 429s; honor Retry-After.
- Configure a fallback model/provider for upstream 5xx outages.
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
- Anthropic API error
- Gemini HTTP error (status )
- OpenRouter API error
- OpenRouter API error
- Aborting session for quota guard
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)