thedotmack/claude-mem · error · ServerClassifiedProviderError

auth_invalid

auth_invalid

Error message

OpenRouter API key not configured

What it means

Thrown by the OpenRouterObservationProvider constructor when options.apiKey is falsy. It is a ServerClassifiedProviderError with kind 'auth_invalid', so the OpenRouter generation provider refuses to construct without credentials. Model is passed verbatim (per #2393) so arbitrary OpenAI-compatible ids work, but the key itself is mandatory.

Source

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

interface OpenRouterResponse {
  choices?: Array<{ message?: { content?: string } }>;
  usage?: { total_tokens?: number };
  error?: { code?: string | number; message?: string };
}

export class OpenRouterObservationProvider implements ServerGenerationProvider {
  readonly providerLabel = 'openrouter' as const;
  private readonly apiKey: string;
  private readonly model: string;
  private readonly apiUrl: string;
  private readonly maxOutputTokens: number;
  private readonly siteUrl: string;
  private readonly appName: string;
  private readonly fetchImpl: typeof fetch;

  constructor(options: OpenRouterObservationProviderOptions) {
    if (!options.apiKey) {
      throw new ServerClassifiedProviderError('OpenRouter API key not configured', {
        kind: 'auth_invalid',
        cause: new Error('apiKey is required'),
      });
    }
    this.apiKey = options.apiKey;
    // Model is passed verbatim so arbitrary OpenAI-compatible ids work. #2393.
    this.model = options.model ?? DEFAULT_MODEL;
    this.apiUrl = resolveOpenRouterChatCompletionsUrl(options.baseUrl);
    this.maxOutputTokens = options.maxOutputTokens ?? 4096;
    this.siteUrl = options.siteUrl ?? 'https://github.com/thedotmack/claude-mem';
    this.appName = options.appName ?? 'claude-mem';
    this.fetchImpl = options.fetchImpl ?? fetch;
  }

  async generate(
    context: ServerGenerationContext,
    signal?: AbortSignal,
  ): Promise<ServerGenerationResult> {

View on GitHub (pinned to d768ba3643)

Solutions

  1. Set OPENROUTER_API_KEY (or the configured setting) to a valid key from openrouter.ai before launch.
  2. Switch providers if openrouter was selected by mistake.
  3. Verify the settings/env layer resolves the key (check length only).
  4. Re-run after fixing config.

Example fix

// before: no key supplied
new OpenRouterObservationProvider({ /* apiKey omitted */ });
// after
new OpenRouterObservationProvider({ apiKey: process.env.OPENROUTER_API_KEY });
// plus: export OPENROUTER_API_KEY=sk-or-... before launch
Defensive patterns

Strategy: validation

Validate before calling

function resolveOpenRouterKey(): string {
  const key = process.env.OPENROUTER_API_KEY ?? readSetting('openRouterApiKey');
  if (!key || !key.trim()) {
    throw new Error('OPENROUTER_API_KEY is not set; cannot use the openrouter provider');
  }
  return key.trim();
}

Type guard

function hasApiKey(opts: unknown): opts is { apiKey: string } {
  return typeof (opts as { apiKey?: unknown })?.apiKey === 'string'
    && (opts as { apiKey: string }).apiKey.trim().length > 0;
}

Try / catch

try {
  provider = new OpenRouterObservationProvider({ apiKey });
} catch (error) {
  if (error instanceof ServerClassifiedProviderError && error.kind === 'auth_invalid') {
    // choose another provider or exit with guidance
  } else throw error;
}

Prevention

When it happens

Trigger: Instantiating OpenRouterObservationProvider with an empty/undefined apiKey; the configured provider is 'openrouter' but the key setting is unset; the settings layer resolved the key to an empty string.

Common situations: Operator selects openrouter but never exports OPENROUTER_API_KEY. A .env file isn't loaded. The key was revoked at openrouter.ai. Local-dev bypass masks the gap until construction.

Related errors


AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12). Data as JSON: /api/errors/ba9457800973d837. Report an issue: GitHub.