{"record":{"id":"889b643fca1ca096","repo":"thedotmack/claude-mem","slug":"missing-api-key-889b64","errorCode":"missing_api_key","errorMessage":"Server API key is not configured (CLAUDE_MEM_SERVER_API_KEY).","messagePattern":"Server API key is not configured \\(CLAUDE_MEM_SERVER_API_KEY\\)\\.","errorType":"error_code","errorClass":"ServerClientError","httpStatus":null,"severity":"error","filePath":"src/services/hooks/server-client.ts","lineNumber":349,"sourceCode":"      projectId: input.projectId,\n      sourceType: input.sourceType,\n      eventType: input.eventType,\n      occurredAtEpoch: input.occurredAtEpoch,\n      ...(input.serverSessionId !== undefined ? { serverSessionId: input.serverSessionId } : {}),\n      ...(input.contentSessionId !== undefined ? { contentSessionId: input.contentSessionId } : {}),\n      ...(input.memorySessionId !== undefined ? { memorySessionId: input.memorySessionId } : {}),\n      ...(input.platformSource !== undefined ? { platformSource: normalizePlatformSourceField(input.platformSource) } : {}),\n      ...(input.payload !== undefined ? { payload: input.payload } : {}),\n    };\n  }\n\n  private async request<T>(\n    method: 'GET' | 'POST',\n    path: string,\n    body?: unknown,\n  ): Promise<T> {\n    if (!this.apiKey || !this.apiKey.trim()) {\n      throw new ServerClientError(\n        'missing_api_key',\n        'Server API key is not configured (CLAUDE_MEM_SERVER_API_KEY).',\n      );\n    }\n\n    const url = `${this.baseUrl}${path}`;\n    const init: RequestInit = {\n      method,\n      headers: {\n        'Content-Type': 'application/json',\n        Authorization: `Bearer ${this.apiKey}`,\n      },\n    };\n    if (body !== undefined) {\n      init.body = JSON.stringify(body);\n    }\n\n    let response: Response;","sourceCodeStart":331,"sourceCodeEnd":367,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d768ba364302d12b76e69e4f021f0bb1d2d50ed6/src/services/hooks/server-client.ts#L331-L367","documentation":"The ServerClient is the HTTP client hook subcommands use to reach the server runtime's /v1/* endpoints when server mode is selected. Before every request it checks that a non-empty API key was supplied at construction; the key normally originates from CLAUDE_MEM_SERVER_API_KEY. The guard ensures hooks never fire an unauthenticated request and, because missing_api_key is fallback-eligible (isFallbackEligible), lets the hook handler transparently fall back to the local worker path instead of hard-failing.","triggerScenarios":"Any ServerClient method (startSession, recordEvent, endSession, addObservation, searchObservations, contextObservations, getJobStatus) is invoked while this.apiKey is undefined, empty string, or whitespace-only after trim(). The check runs at the top of the private request<T>() method, so every endpoint hits it.","commonSituations":"CLAUDE_MEM_SERVER_API_KEY env var was never exported in the shell that launched the worker/hooks; the installer wrote an empty quoted value into the settings file; server mode was selected during install but no API key was provisioned yet; CI/container environment forgot to inject the secret; the key was loaded from the wrong settings file path.","solutions":["Export CLAUDE_MEM_SERVER_API_KEY with a valid server-issued key in the environment that runs claude-mem (and re-run the install so it is persisted to the settings file).","Confirm the key is non-empty after trimming by inspecting the settings file the installer reads (look for an empty value or mismatched quotes around the key).","If you did not intend server mode, re-run the installer and select the local/worker runtime so ServerClient is never constructed.","Restart the worker after setting the key so the running process picks up the new env value."],"exampleFix":"// before\nconst client = new ServerClient({ serverBaseUrl, apiKey: settings.CLAUDE_MEM_SERVER_API_KEY });\n// settings.CLAUDE_MEM_SERVER_API_KEY === ''  -> throws missing_api_key\n\n// after\nconst apiKey = (settings.CLAUDE_MEM_SERVER_API_KEY ?? '').trim();\nif (!apiKey) throw new Error('Set CLAUDE_MEM_SERVER_API_KEY before enabling server mode');\nconst client = new ServerClient({ serverBaseUrl, apiKey });","handlingStrategy":"validation","validationCode":"import { isServerClientError, ServerClient } from './server-client.js';\n\nfunction makeClient(serverBaseUrl: string, apiKey?: string): ServerClient {\n  const key = (apiKey ?? '').trim();\n  if (!key) {\n    throw new Error('CLAUDE_MEM_SERVER_API_KEY is missing; cannot use server mode');\n  }\n  return new ServerClient({ serverBaseUrl, apiKey: key });\n}","typeGuard":"import { ServerClientError } from './server-client.js';\n\nfunction isMissingApiKey(e: unknown): boolean {\n  return e instanceof ServerClientError && e.kind === 'missing_api_key';\n}","tryCatchPattern":"try {\n  await client.recordEvent(input);\n} catch (e) {\n  if (e instanceof ServerClientError && e.isFallbackEligible()) {\n    await worker.recordEvent(input); // missing_api_key is fallback-eligible\n    return;\n  }\n  throw e;\n}","preventionTips":["Centralize ServerClient construction in one factory that validates the API key and fails loudly at startup.","Persist CLAUDE_MEM_SERVER_API_KEY through the installer so the worker reads it on every launch.","Log a clear message at worker boot when server mode is on but the key is empty."],"tags":["config","authentication","server-client","env-vars","fallback-eligible"],"backgroundTag":null,"analyzedSha":"d768ba364302d12b76e69e4f021f0bb1d2d50ed6","analyzedAt":"2026-08-12T23:52:55.241Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}