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 Postgres auth middleware when no API key is present on the request. Same contract as the SQLite variant: the key must arrive as Authorization: Bearer <key> or X-Api-Key: <key>; when local-dev is off (or localDevTeamId unset and dev mode inactive) and rawKey is empty, the middleware rejects with 401 before touching Postgres.

Source

Thrown at src/server/middleware/postgres-auth.ts:91

    && hasLoopbackHostHeader(req)
    && !hasForwardedClientHeaders(req)
  ) {
    const ctx: AuthContext = {
      userId: null,
      organizationId: null,
      teamId: options.localDevTeamId ?? null,
      projectId: null,
      scopes: ['local-dev'],
      apiKeyId: null,
      mode: 'local-dev',
    };
    req.authContext = ctx;
    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 = await verifyPostgresApiKey(pool, rawKey, options.requiredScopes ?? []);
  if (!verified) {
    res.status(403).json({ error: 'Forbidden', message: 'Invalid API key or insufficient scope' });
    return;
  }

  const ctx: AuthContext = {
    userId: null,
    organizationId: null,
    teamId: verified.teamId,
    projectId: verified.projectId,
    scopes: verified.scopes,

View on GitHub (pinned to e2d1df569a)

Solutions

  1. Attach the key: Authorization: Bearer <key> or X-Api-Key: <key> on every request.
  2. Verify the client's key env var is set in the runtime that actually issues requests.
  3. Ensure proxies/ingress forward the Authorization header to the app.
  4. Use local-dev mode only for local runs, and configure localDevTeamId if you need unauthenticated local requests.

Example fix

// before
const res = await fetch(`${base}/v1/events`, { method: 'POST', body }); // 401

// after
const res = await fetch(`${base}/v1/events`, {
  method: 'POST',
  headers: { 'X-Api-Key': process.env.API_KEY!, 'Content-Type': 'application/json' },
  body,
});
Defensive patterns

Strategy: validation

Validate before calling

const apiKey = process.env.API_KEY;
if (!apiKey) {
  throw new Error('API_KEY unset; postgres-auth will reject with 401 before hitting the database');
}
const headers = { Authorization: `Bearer ${apiKey}` };

Type guard

interface UnauthorizedBody { error: string; message: string }
function isMissingKey401(res: Response, body: unknown): boolean {
  return res.status === 401 && typeof body === 'object' && body !== null &&
    (body as UnauthorizedBody).error === 'Unauthorized';
}

Prevention

When it happens

Trigger: Any request to a postgres-backed route (e.g. /v1/events) with neither auth header; a gateway or sidecar that drops the Authorization header; client configured with an unset key env var so it sends nothing.

Common situations: Deploying against Postgres for the first time and reusing scripts written for local-dev mode where no header was needed; .env missing API_KEY in the container; ingress stripping Authorization unless explicitly forwarded.

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/97d99b06c78f819d. Report an issue: GitHub.