{"record":{"id":"65d0b54b4fe87aa9","repo":"thedotmack/claude-mem","slug":"auth-invalid","errorCode":"auth_invalid","errorMessage":"Anthropic API key not configured","messagePattern":"Anthropic API key not configured","errorType":"exception","errorClass":"ServerClassifiedProviderError","httpStatus":null,"severity":"error","filePath":"src/server/generation/providers/ClaudeObservationProvider.ts","lineNumber":47,"sourceCode":"  fetchImpl?: typeof fetch;\n}\n\ninterface AnthropicMessagesResponse {\n  content?: Array<{ type?: string; text?: string }>;\n  usage?: { input_tokens?: number; output_tokens?: number };\n  error?: { type?: string; message?: string };\n}\n\nexport class ClaudeObservationProvider implements ServerGenerationProvider {\n  readonly providerLabel = 'claude' as const;\n  private readonly apiKey: string;\n  private readonly model: string;\n  private readonly maxOutputTokens: number;\n  private readonly fetchImpl: typeof fetch;\n\n  constructor(options: ClaudeObservationProviderOptions) {\n    if (!options.apiKey) {\n      throw new ServerClassifiedProviderError('Anthropic API key not configured', {\n        kind: 'auth_invalid',\n        cause: new Error('apiKey is required'),\n      });\n    }\n    this.apiKey = options.apiKey;\n    this.model = options.model ?? DEFAULT_MODEL;\n    this.maxOutputTokens = options.maxOutputTokens ?? 4096;\n    this.fetchImpl = options.fetchImpl ?? fetch;\n  }\n\n  async generate(\n    context: ServerGenerationContext,\n    signal?: AbortSignal,\n  ): Promise<ServerGenerationResult> {\n    const { prompt, skippedAll } = buildServerGenerationPrompt(context);\n    if (skippedAll) {\n      // All events were scrubbed by privacy stripping. Don't bill the\n      // provider — return a synthetic skip response that parser accepts.","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d768ba364302d12b76e69e4f021f0bb1d2d50ed6/src/server/generation/providers/ClaudeObservationProvider.ts#L29-L65","documentation":"Thrown by the ClaudeObservationProvider constructor when options.apiKey is falsy. It is a ServerClassifiedProviderError with kind 'auth_invalid', signalling that the Claude (Anthropic) generation provider cannot make authenticated calls. Constructing the provider is the trust boundary — auth is validated up front rather than failing on the first request.","triggerScenarios":"Instantiating ClaudeObservationProvider with an empty/undefined apiKey; the configured provider is 'claude' but ANTHROPIC_API_KEY is unset; the settings layer resolved the key to an empty string.","commonSituations":"Operator selects the claude generation provider in config but never exports ANTHROPIC_API_KEY. A .env file isn't loaded. The key was deleted from the secret store. Local-dev bypass masks the missing key until the provider is first built.","solutions":["Set ANTHROPIC_API_KEY in the environment (or settings) to a valid key before starting the worker/server.","If you intended a different backend, switch the configured provider to gemini/openrouter and supply that provider's key instead.","Verify the settings manager and .env loading actually surface the key (log the resolved length, not the value).","Re-run with a known-good key from the Anthropic console."],"exampleFix":"// before: provider built with no key\nnew ClaudeObservationProvider({ /* apiKey omitted */ });\n// after\nnew ClaudeObservationProvider({ apiKey: process.env.ANTHROPIC_API_KEY });\n// plus: export ANTHROPIC_API_KEY=sk-ant-... before launch","handlingStrategy":"validation","validationCode":"function resolveClaudeKey(): string {\n  const key = process.env.ANTHROPIC_API_KEY ?? readSetting('anthropicApiKey');\n  if (!key || !key.trim()) {\n    throw new Error('ANTHROPIC_API_KEY is not set; cannot use the claude provider');\n  }\n  return key.trim();\n}\n// pass resolveClaudeKey() into new ClaudeObservationProvider({ apiKey })","typeGuard":"function hasApiKey(opts: unknown): opts is { apiKey: string } {\n  return typeof (opts as { apiKey?: unknown })?.apiKey === 'string'\n    && (opts as { apiKey: string }).apiKey.trim().length > 0;\n}","tryCatchPattern":"try {\n  provider = new ClaudeObservationProvider({ apiKey });\n} catch (error) {\n  if (error instanceof ServerClassifiedProviderError && error.kind === 'auth_invalid') {\n    // fall back to a different configured provider, or exit with a clear message\n  } else throw error;\n}","preventionTips":["Validate required API keys at startup (length only, never log the value) and fail fast with a clear message.","Load .env consistently via the same loader the app uses in all environments.","Store keys in a secret manager; detect revocation via a cheap auth probe."],"tags":["auth","claude","provider","config","api-key"],"backgroundTag":null,"analyzedSha":"d768ba364302d12b76e69e4f021f0bb1d2d50ed6","analyzedAt":"2026-08-12T23:52:55.241Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}