{"record":{"id":"efe43ab24351d8ab","repo":"thedotmack/claude-mem","slug":"missing-api-key","errorCode":"missing_api_key","errorMessage":"${toolName}: ${resolution.reason}","messagePattern":"\\$\\{toolName\\}: \\$\\{resolution\\.reason\\}","errorType":"error_code","errorClass":"ServerClientError","httpStatus":null,"severity":"error","filePath":"src/servers/mcp-server.ts","lineNumber":212,"sourceCode":"function formatJsonResult(payload: unknown): { content: Array<{ type: 'text'; text: string }> } {\n  return {\n    content: [{\n      type: 'text' as const,\n      text: JSON.stringify(payload, null, 2),\n    }],\n  };\n}\n\nfunction requireServerForObservationTool(toolName: string): ServerAvailable {\n  const resolution = resolveServerToolContext();\n  if (!resolution) {\n    throw new ServerClientError(\n      'transport',\n      `${toolName} requires CLAUDE_MEM_RUNTIME=server. Current runtime is \"worker\"; use the existing search/timeline/get_observations tools for worker-mode memory access.`,\n    );\n  }\n  if (!resolution.available) {\n    throw new ServerClientError('missing_api_key', `${toolName}: ${resolution.reason}`);\n  }\n  return resolution;\n}\n\nfunction wrapHandler<Args>(\n  toolName: string,\n  execute: (args: Args) => Promise<{ content: Array<{ type: 'text'; text: string }> }>,\n): (args: Args) => Promise<{ content: Array<{ type: 'text'; text: string }>; isError?: boolean }> {\n  return async (args: Args) => {\n    try {\n      return await execute(args);\n    } catch (error) {\n      const err = error instanceof Error ? error : new Error(String(error));\n      logger.warn('SYSTEM', `${toolName} failed`, undefined, err);\n      return formatToolError(error);\n    }\n  };\n}","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d768ba364302d12b76e69e4f021f0bb1d2d50ed6/src/servers/mcp-server.ts#L194-L230","documentation":"Thrown by requireServerForObservationTool() when the server runtime IS selected (selectRuntime() === 'server') but buildServerContext() returns null, meaning required configuration is incomplete. The ServerClientError kind is 'missing_api_key' and the reason text is the one built in resolveServerToolContext(): 'server runtime is selected but configuration is incomplete (missing url, api key, or project id)'.","triggerScenarios":"CLAUDE_MEM_RUNTIME=server is set, but at least one of serverBaseUrl, apiKey, or projectId is missing/empty, so buildServerContext() yields null and resolution.available is false. Any observation_* tool call then hits this guard.","commonSituations":"User set runtime=server but forgot the API key; serverBaseUrl points at the wrong host; the project id was never bootstrapped; key was revoked/expired and the bootstrap step that writes it did not run; settings file partially overwritten.","solutions":["Run the server bootstrap/credential provisioning step so serverBaseUrl, apiKey, and projectId are all written into settings.","Open the settings file (or CLAUDE_MEM_SERVER_* env) and confirm all three of url, apiKey, projectId are present and non-empty.","Confirm the API key is still valid by calling the server /v1 health or a cheap endpoint; re-bootstrap if revoked.","If you intentionally have no server, set CLAUDE_MEM_RUNTIME=worker to stop advertising the server-only tools."],"exampleFix":"// before — runtime=server but no apiKey in settings\n{ \"runtime\": \"server\", \"serverBaseUrl\": \"https://api.example.com\" }\n// → ServerClientError(missing_api_key, '...missing url, api key, or project id')\n\n// after\n{\n  \"runtime\": \"server\",\n  \"serverBaseUrl\": \"https://api.example.com\",\n  \"apiKey\": \"cmem_<from-bootstrap>\",\n  \"projectId\": \"<from-bootstrap>\"\n}","handlingStrategy":"validation","validationCode":"// Validate all three required server-config fields before calling observation_* tools.\nconst { serverBaseUrl, apiKey, projectId } = readSettings();\nconst ok = Boolean(serverBaseUrl && apiKey && projectId);\nif (!ok) {\n  throw new Error('Server runtime selected but url/apiKey/projectId missing — run bootstrap');\n}","typeGuard":"function serverContextComplete(ctx: { serverBaseUrl?: string; apiKey?: string; projectId?: string } | null): ctx is { serverBaseUrl: string; apiKey: string; projectId: string } {\n  return !!ctx && !!ctx.serverBaseUrl && !!ctx.apiKey && !!ctx.projectId;\n}","tryCatchPattern":"try {\n  await tools.observation_add({ content });\n} catch (e) {\n  if (e instanceof ServerClientError && e.kind === 'missing_api_key') {\n    // prompt the user to run bootstrap / fix settings\n    notifyUser('Server config incomplete: ' + e.message);\n  } else throw e;\n}","preventionTips":["Run the bootstrap step as part of install so url/apiKey/projectId are written together.","Validate the settings triple on startup and fail fast with a clear message rather than at first tool call.","Re-verify the API key periodically (cheap health call) since keys can be revoked server-side."],"tags":["mcp","runtime","config","server-mode","auth"],"backgroundTag":null,"analyzedSha":"d768ba364302d12b76e69e4f021f0bb1d2d50ed6","analyzedAt":"2026-08-12T23:52:55.241Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}