thedotmack/claude-mem · error

Forbidden

Forbidden

Error message

Invalid API key or insufficient scope

What it means

403 from the SQLite auth middleware: a key was presented but verifyServerApiKey returned null, meaning the key does not exist, is revoked/expired, or lacks one of the route's requiredScopes (e.g. memories:write on the compat summarize route). Note the distinction from 401 — 401 means no key, 403 means the key failed verification or scope.

Source

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

        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,
      apiKeyId: verified.record.id,
      mode: 'api-key',
    };
    next();
  };
}

View on GitHub (pinned to e2d1df569a)

Solutions

  1. Re-copy the full key with no trailing whitespace/newline and retry.
  2. Confirm the key exists and is active in the server's database (keys table / key management endpoint).
  3. Check the route's requiredScopes and mint a key that includes them (e.g. memories:write for summarize).
  4. If keys were rotated, update the client's stored credential.
Defensive patterns

Strategy: validation

Validate before calling

// Verify key validity + scopes before real work (cheapest probe: an authenticated read)
const probe = await fetch(`${base}/v1/keys`, { headers: { 'X-Api-Key': key } });
if (probe.status === 403) {
  throw new Error('Key invalid or missing required scope — remint before continuing');
}

Type guard

function isInvalidKeyResponse(res: Response): boolean {
  return res.status === 403;
}
interface ForbiddenBody { error: string; message: string }
function isInvalidKeyBody(body: unknown): body is ForbiddenBody {
  return typeof body === 'object' && body !== null &&
    (body as ForbiddenBody).error === 'Forbidden' &&
    (body as ForbiddenBody).message === 'Invalid API key or insufficient scope';
}

Prevention

When it happens

Trigger: Request with a mistyped or truncated key; a key deleted or rotated server-side while clients still hold the old value; a read-only key (no memories:write) hitting POST /api/sessions/summarize whose writeAuth requires memories:write; key exists in a different database/environment than the one serving the request.

Common situations: Key rotation deployed to server but clients cached the old key; copy-paste lost characters or gained whitespace/newline; dev pointed at prod with a dev-database key; scope set at mint time omitted the write scope the endpoint needs.

Understand the failure class

Related errors


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