thedotmack/claude-mem · error

classifyOpenRouterError

Error message

classifyOpenRouterError({ cause: err })

What it means

In OpenRouterProvider.queryOpenRouterMultiTurn, errors thrown by fetchChatCompletion — network failures, DNS errors, connection resets, timeouts, aborts — are passed through classifyOpenRouterError, which wraps the cause into a structured, classified error object before it propagates. This mirrors the Gemini provider pattern and enables consistent retry/messaging logic for OpenRouter requests.

Solutions

  1. Catch the classified error and branch on its classification before deciding to retry
  2. Auto-retry network-classified errors with exponential backoff; never retry user aborts
  3. Check connectivity and proxy/firewall rules for openrouter.ai on persistent failures
  4. Surface the wrapped cause's message for diagnosis when classification is unknown

Example fix

// before
const res = await provider.query(messages);
// after
try {
  const res = await provider.query(messages);
} catch (e) {
  if ((e as OpenRouterError).classification === 'network' && !isAbort(e)) await backoffRetry();
  throw e;
}
Defensive patterns

Strategy: retry

Validate before calling

// Reachability pre-check for OpenRouter
const ok = await fetch('https://openrouter.ai/api/v1/models', { method: 'HEAD' })
  .then(() => true).catch(() => false);
if (!ok) throw new Error('openrouter.ai unreachable — check network/proxy');

Type guard

const isOpenRouterNetworkError = (e: unknown): e is { classification: 'network'; cause: Error } =>
  typeof e === 'object' && e !== null && (e as any).classification === 'network';

Try / catch

try {
  const res = await provider.query(messages);
} catch (e) {
  if (isOpenRouterNetworkError(e) && e.cause.name !== 'AbortError') {
    return backoffRetry(() => provider.query(messages), { retries: 3 });
  }
  throw e;
}

Prevention

When it happens

Trigger: The fetch to OpenRouter's /chat/completions endpoint fails before receiving an HTTP response: network outage, DNS failure, connection refused, TLS error, or the attemptSignal aborted the request.

Common situations: Flaky network or VPN drop mid-request; firewall or proxy blocking openrouter.ai; request aborted by user cancellation; long completions exceeding a client-side timeout.

Related errors


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

Appendix: source

Thrown at src/services/worker/OpenRouterProvider.ts:542

    const messages = this.conversationToOpenAIMessages(history);
    const totalChars = history.reduce((sum, m) => sum + m.content.length, 0);
    const estimatedTokens = this.estimateTokens(messages.map(m => m.content).join(''));

    logger.debug('SDK', `Querying OpenRouter multi-turn (${model})`, {
      turns: history.length,
      totalChars,
      estimatedTokens
    });

    let priorRequestId: string | null = null;

    const data = await withRetry<OpenRouterResponse>(async (attemptSignal) => {
      let response: Response;
      try {
        response = await this.fetchChatCompletion(apiUrl, apiKey, model, fallbackModels, messages, siteUrl, appName, priorRequestId, attemptSignal, plainText);
      } catch (networkError: unknown) {
        const err = networkError instanceof Error ? networkError : new Error(String(networkError));
        throw classifyOpenRouterError({ cause: err });
      }

      const requestId = response.headers.get('x-request-id') ?? response.headers.get('x-openrouter-request-id');
      if (requestId) {
        priorRequestId = requestId;
      } else {
        logger.debug('SDK', 'OpenRouter response missing request-id header; retry dedup is best-effort');
      }

      if (!response.ok) {
        const errorText = await response.text();
        throw classifyOpenRouterError({
          status: response.status,
          bodyText: errorText,
          headers: response.headers,
          cause: new Error(`OpenRouter API error: ${response.status} - ${errorText}`),
          ...(requestId ? { requestId } : {}),
        });

View on GitHub (pinned to d8bc9755e7)