thedotmack/claude-mem · error

Unauthorized

Unauthorized

Error message

Missing API key (Authorization: Bearer <key> or X-Api-Key: <key>)

What it means

401 from the SQLite auth middleware when the request carries no API key at all. The middleware extracts the key from Authorization: Bearer <key> or X-Api-Key: <key>; when local-dev mode is disabled and rawKey is empty, it rejects immediately with this message documenting both accepted header forms.

Source

Thrown at src/server/middleware/auth.ts:71

      && isLocalhost(req)
      && hasLoopbackHostHeader(req)
      && !hasForwardedClientHeaders(req)
    ) {
      req.authContext = {
        userId: null,
        organizationId: null,
        teamId: null,
        projectId: null,
        scopes: ['local-dev'],
        apiKeyId: null,
        mode: 'local-dev',
      };
      next();
      return;
    }

    if (!rawKey) {
      res.status(401).json({
        error: 'Unauthorized',
        message: 'Missing API key (Authorization: Bearer <key> or X-Api-Key: <key>)',
      });
      return;
    }

    const verified = verifyServerApiKey(getDatabase(), rawKey, options.requiredScopes ?? []);
    if (!verified) {
      res.status(403).json({ error: 'Forbidden', message: 'Invalid API key or insufficient scope' });
      return;
    }

    req.authContext = {
      userId: null,
      organizationId: null,
      teamId: verified.teamId,
      projectId: verified.projectId,
      scopes: verified.scopes,

View on GitHub (pinned to e2d1df569a)

Solutions

  1. Send the key in one of the two accepted headers: Authorization: Bearer <key> or X-Api-Key: <key>.
  2. Check the environment variable that carries the key is actually set in the client's environment (print its presence, never its value).
  3. If you intended to run without keys, enable the server's local-dev mode — but never in production.
  4. Verify the header name spelling and that no proxy strips Authorization on its way to the server.

Example fix

# before
curl -X POST https://host/api/sessions/summarize -d '{}'
// 401 Missing API key

# after
curl -X POST https://host/api/sessions/summarize \
  -H 'Authorization: Bearer sk-...' \
  -H 'Content-Type: application/json' \
  -d '{"contentSessionId":"..."}'
Defensive patterns

Strategy: validation

Validate before calling

function buildHeaders(key: string | undefined): Record<string, string> {
  if (!key) throw new Error('API key is not configured (set API_KEY)');
  return { 'X-Api-Key': key, 'Content-Type': 'application/json' };
}

Type guard

interface UnauthorizedBody { error: string; message: string }
function isMissingApiKey(body: unknown): body is UnauthorizedBody {
  return typeof body === 'object' && body !== null &&
    (body as UnauthorizedBody).error === 'Unauthorized' &&
    (body as UnauthorizedBody).message?.startsWith('Missing API key');
}

Prevention

When it happens

Trigger: Any request to a protected route (e.g. POST /api/sessions/summarize) with neither Authorization nor X-Api-Key header; header name typo like X-API-Key with wrong casing in a raw client (case-insensitive in HTTP, but misspelled names fail); env var holding the key is unset so the client sends 'Bearer undefined'.

Common situations: Forgot to set API_KEY in .env so the client sends an empty/undefined token; curl invocation missing -H; switching a script from local-dev (where no key is needed) to a deployed server where local-dev is off.

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


AI-assisted analysis of thedotmack/claude-mem@e2d1df569a (2026-08-20). Data as JSON: /api/errors/3fb1107f36ca7c0b. Report an issue: GitHub.