thedotmack/claude-mem · error · ServerClassifiedProviderError
auth_invalid
auth_invalid
Error message
OpenRouter API key not configured
What it means
The OpenRouter provider's constructor rejects instantiation when `options.apiKey` is falsy, throwing a ServerClassifiedProviderError with kind 'auth_invalid'. OpenRouter requires a Bearer key for every request, so the provider fails fast rather than sending doomed requests. Raised from the constructor, so the app crashes at wiring time.
Solutions
- Set OPENROUTER_API_KEY in the environment or deployment secrets
- Trace where the options object is built and confirm the correct env var/config key is read and non-empty
- Add a startup config check that fails with a clear message listing required provider keys
- Verify .env loading (dotenv/similar) runs before any provider construction
Example fix
// before
const provider = new OpenRouterObservationProvider({ apiKey: config.openrouter?.key });
// after
const apiKey = process.env.OPENROUTER_API_KEY;
if (!apiKey) throw new Error('OPENROUTER_API_KEY is required');
const provider = new OpenRouterObservationProvider({ apiKey }); Defensive patterns
Strategy: validation
Validate before calling
const apiKey = process.env.OPENROUTER_API_KEY;
if (typeof apiKey !== 'string' || apiKey.trim() === '') {
throw new Error('OPENROUTER_API_KEY must be set to a non-empty string');
}
const provider = new OpenRouterObservationProvider({ apiKey }); Type guard
function hasOpenRouterKey(o: OpenRouterObservationProviderOptions): o is OpenRouterObservationProviderOptions & { apiKey: string } {
return typeof o.apiKey === 'string' && o.apiKey.trim().length > 0;
} Try / catch
try {
provider = new OpenRouterObservationProvider({ apiKey });
} catch (e) {
if (e instanceof ServerClassifiedProviderError && e.kind === 'auth_invalid') {
throw new ConfigError('OpenRouter provider misconfigured: apiKey missing. Set OPENROUTER_API_KEY.');
}
throw e;
} Prevention
- Run a startup config check that lists all missing provider credentials at once
- Keep OPENROUTER_API_KEY in .env.example and deployment secrets templates
- Ensure dotenv/secret loading happens before provider construction
- Trim and type-check key values read from config sources
When it happens
Trigger: Constructing `new OpenRouterObservationProvider({...})` with `apiKey` undefined, null, or empty string — typically because the OPENROUTER_API_KEY environment variable is unset or the wrong config key was read.
Common situations: OPENROUTER_API_KEY not set in the deployment environment; .env file not loaded; key stored under a different env name than the code reads; secrets manager returning undefined for a missing entry; refactoring renamed the option field without updating call sites.
Understand the failure class
Background: "API key is required" / "API key not found" / "No API key was set": the missing-api-key error family across 16 libraries — this error's family across 16 libraries.
Related errors
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/ba9457800973d837.
Report an issue: GitHub.
Appendix: source
Thrown at src/server/generation/providers/OpenRouterObservationProvider.ts:55
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 ?? OPENROUTER_APP_URL;
this.appName = options.appName ?? OPENROUTER_APP_TITLE;
this.fetchImpl = options.fetchImpl ?? fetch;
}
async generate(
context: ServerGenerationContext,
signal?: AbortSignal,
): Promise<ServerGenerationResult> {View on GitHub (pinned to d8bc9755e7)