thedotmack/claude-mem · critical · ServerClassifiedProviderError
auth_invalid
auth_invalid
Error message
Anthropic API key not configured
What it means
ClaudeObservationProvider's constructor validates that an Anthropic API key was supplied in its options and throws a ServerClassifiedProviderError with kind 'auth_invalid' when it is missing/empty. The provider cannot authenticate to Anthropic without a key, so it fails fast at construction time.
Solutions
- Set the ANTHROPIC_API_KEY environment variable (or the settings location the server reads it from) and restart the server.
- Verify the key is actually propagated into the provider options where ClaudeObservationProvider is constructed.
- Catch ServerClassifiedProviderError and check err kind === 'auth_invalid' to give the user a clear configuration prompt instead of a crash.
Example fix
// before
new ClaudeObservationProvider({ model: 'claude-sonnet-4' })
// after
new ClaudeObservationProvider({ apiKey: process.env.ANTHROPIC_API_KEY!, model: 'claude-sonnet-4' }) Defensive patterns
Strategy: validation
Validate before calling
if (!process.env.ANTHROPIC_API_KEY) {
throw new Error('ANTHROPIC_API_KEY must be set before starting the server');
}
new ClaudeObservationProvider({ apiKey: process.env.ANTHROPIC_API_KEY }); Type guard
const hasApiKey = (o: ClaudeObservationProviderOptions): o is ClaudeObservationProviderOptions & { apiKey: string } =>
typeof o.apiKey === 'string' && o.apiKey.length > 0; Try / catch
try {
provider = new ClaudeObservationProvider(opts);
} catch (e) {
if (e instanceof ServerClassifiedProviderError && e.kind === 'auth_invalid') {
console.error('Set ANTHROPIC_API_KEY and restart the server.');
}
} Prevention
- Set ANTHROPIC_API_KEY in the server's environment or secret manager before startup.
- Fail startup early with a clear message if the key is absent.
- Never construct generation providers with possibly-undefined credential fields; validate config at load time.
When it happens
Trigger: Constructing new ClaudeObservationProvider(options) with options.apiKey undefined, null, or an empty string — typically because ANTHROPIC_API_KEY was not set when server config was built.
Common situations: Server started without ANTHROPIC_API_KEY in the environment, key removed from settings, or provider options assembled programmatically without passing the credential through.
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
- Failed to bootstrap server API key
- Anthropic API error
- Anthropic API error
- Anthropic API rejected request with HTTP 400: this model…
- API key is scoped to a different project
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/65d0b54b4fe87aa9.
Report an issue: GitHub.
Appendix: source
Thrown at src/server/generation/providers/ClaudeObservationProvider.ts:47
fetchImpl?: typeof fetch;
}
interface AnthropicMessagesResponse {
content?: Array<{ type?: string; text?: string }>;
usage?: { input_tokens?: number; output_tokens?: number };
error?: { type?: string; message?: string };
}
export class ClaudeObservationProvider implements ServerGenerationProvider {
readonly providerLabel = 'claude' as const;
private readonly apiKey: string;
private readonly model: string;
private readonly maxOutputTokens: number;
private readonly fetchImpl: typeof fetch;
constructor(options: ClaudeObservationProviderOptions) {
if (!options.apiKey) {
throw new ServerClassifiedProviderError('Anthropic API key not configured', {
kind: 'auth_invalid',
cause: new Error('apiKey is required'),
});
}
this.apiKey = options.apiKey;
this.model = options.model ?? DEFAULT_MODEL;
this.maxOutputTokens = options.maxOutputTokens ?? 4096;
this.fetchImpl = options.fetchImpl ?? fetch;
}
async generate(
context: ServerGenerationContext,
signal?: AbortSignal,
): Promise<ServerGenerationResult> {
const { prompt, skippedAll } = buildServerGenerationPrompt(context);
if (skippedAll) {
// All events were scrubbed by privacy stripping. Don't bill the
// provider — return a synthetic skip response that parser accepts.View on GitHub (pinned to d8bc9755e7)