thedotmack/claude-mem · error · ServerClientError

missing_api_key

missing_api_key

Error message

Server API key is not configured (CLAUDE_MEM_SERVER_API_KEY).

What it means

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.

Source

Thrown at src/services/hooks/server-client.ts:349

      projectId: input.projectId,
      sourceType: input.sourceType,
      eventType: input.eventType,
      occurredAtEpoch: input.occurredAtEpoch,
      ...(input.serverSessionId !== undefined ? { serverSessionId: input.serverSessionId } : {}),
      ...(input.contentSessionId !== undefined ? { contentSessionId: input.contentSessionId } : {}),
      ...(input.memorySessionId !== undefined ? { memorySessionId: input.memorySessionId } : {}),
      ...(input.platformSource !== undefined ? { platformSource: normalizePlatformSourceField(input.platformSource) } : {}),
      ...(input.payload !== undefined ? { payload: input.payload } : {}),
    };
  }

  private async request<T>(
    method: 'GET' | 'POST',
    path: string,
    body?: unknown,
  ): Promise<T> {
    if (!this.apiKey || !this.apiKey.trim()) {
      throw new ServerClientError(
        'missing_api_key',
        'Server API key is not configured (CLAUDE_MEM_SERVER_API_KEY).',
      );
    }

    const url = `${this.baseUrl}${path}`;
    const init: RequestInit = {
      method,
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${this.apiKey}`,
      },
    };
    if (body !== undefined) {
      init.body = JSON.stringify(body);
    }

    let response: Response;

View on GitHub (pinned to d768ba3643)

Solutions

  1. 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).
  2. 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).
  3. If you did not intend server mode, re-run the installer and select the local/worker runtime so ServerClient is never constructed.
  4. Restart the worker after setting the key so the running process picks up the new env value.

Example fix

// before
const client = new ServerClient({ serverBaseUrl, apiKey: settings.CLAUDE_MEM_SERVER_API_KEY });
// settings.CLAUDE_MEM_SERVER_API_KEY === ''  -> throws missing_api_key

// after
const apiKey = (settings.CLAUDE_MEM_SERVER_API_KEY ?? '').trim();
if (!apiKey) throw new Error('Set CLAUDE_MEM_SERVER_API_KEY before enabling server mode');
const client = new ServerClient({ serverBaseUrl, apiKey });
Defensive patterns

Strategy: validation

Validate before calling

import { isServerClientError, ServerClient } from './server-client.js';

function makeClient(serverBaseUrl: string, apiKey?: string): ServerClient {
  const key = (apiKey ?? '').trim();
  if (!key) {
    throw new Error('CLAUDE_MEM_SERVER_API_KEY is missing; cannot use server mode');
  }
  return new ServerClient({ serverBaseUrl, apiKey: key });
}

Type guard

import { ServerClientError } from './server-client.js';

function isMissingApiKey(e: unknown): boolean {
  return e instanceof ServerClientError && e.kind === 'missing_api_key';
}

Try / catch

try {
  await client.recordEvent(input);
} catch (e) {
  if (e instanceof ServerClientError && e.isFallbackEligible()) {
    await worker.recordEvent(input); // missing_api_key is fallback-eligible
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12). Data as JSON: /api/errors/889b643fca1ca096. Report an issue: GitHub.