{"record":{"id":"235a44a4d18ec50e","repo":"thedotmack/claude-mem","slug":"quota-exceeded","errorCode":"quota_exceeded","errorMessage":"Monthly ${opts.kind} quota reached (${opts.cap})","messagePattern":"Monthly (.+?) quota reached \\((.+?)\\)","errorType":"http","errorClass":null,"httpStatus":402,"severity":"warning","filePath":"src/server/middleware/rate-limit.ts","lineNumber":77,"sourceCode":"      logger.warn('HTTP', 'rate limit check failed; allowing request (fail open)', {\n        error: error instanceof Error ? error.message : String(error),\n      });\n      return next();\n    }\n  };\n}\n\n/** Checks month-to-date usage against the cap and answers 402 / next(). */\nasync function enforceMonthlyQuota(\n  repo: PostgresUsageRepository,\n  opts: { kind: string; cap: number },\n  teamId: string,\n  res: Response,\n  next: NextFunction,\n): Promise<Response | void> {\n  const used = await repo.total({ teamId, kind: opts.kind, since: monthStartUtc(new Date()) });\n  if (used >= opts.cap) {\n    return res.status(402).json({\n      error: 'quota_exceeded',\n      message: `Monthly ${opts.kind} quota reached (${opts.cap})`,\n      used,\n      cap: opts.cap,\n    });\n  }\n  return next();\n}\n\n/** Monthly per-team quota on a usage `kind` (e.g. 'request'). 402 when reached. */\nexport function requireMonthlyQuota(pool: PostgresPool, opts: { kind: string; cap: number }): RequestHandler {\n  const repo = new PostgresUsageRepository(pool);\n  return async (req: Request, res: Response, next: NextFunction) => {\n    const teamId = req.authContext?.teamId;\n    if (!teamId) return next();\n    try {\n      return await enforceMonthlyQuota(repo, opts, teamId, res, next);\n    } catch (error) {","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/e2d1df569a8f04075d40e92461128ece7cf04c82/src/server/middleware/rate-limit.ts#L59-L95","documentation":"402 Payment Required from the monthly quota middleware: month-to-date usage of the given kind for the team (summed since monthStartUtc) has reached opts.cap, so further requests are refused until the calendar month rolls over. The body includes used and cap so clients can compute remaining headroom; unlike the rate limiter this is not time-retryable within the month.","triggerScenarios":"Team's counted requests for the month hit the configured cap and any further metered call returns 402 with used >= cap; heavy batch ingestion early in the month exhausting the budget; quota cap lowered while usage already above it.","commonSituations":"Free-tier teams hitting monthly request caps mid-month; a runaway loop burning quota; ops reduced a cap retroactively and every team above it starts failing.","solutions":["Raise the cap for the team/kind server-side if the usage is legitimate.","Reduce counted usage: batch events (fewer metered requests) and drop generate=true where observations are not needed.","Queue work client-side and resume after the month boundary (UTC) when usage resets.","Alert on used/cap ratio from the response body or usage endpoint before hitting the wall."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"// Track headroom from the last 402 body (used/cap) or a usage endpoint\nfunction quotaHeadroom(used: number, cap: number): number {\n  return cap - used;\n}\nif (quotaHeadroom(used, cap) < events.length) {\n  return queueForNextMonth(events); // local spool, resume at UTC month start\n}","typeGuard":"interface QuotaBody { error: string; used: number; cap: number }\nfunction isQuotaExceeded(res: Response, body: unknown): body is QuotaBody {\n  return res.status === 402 && typeof body === 'object' && body !== null &&\n    (body as QuotaBody).error === 'quota_exceeded';\n}","tryCatchPattern":"const res = await fetch(url, init);\nif (res.status === 402) {\n  const body = await res.json();\n  if (isQuotaExceeded(res, body)) {\n    await spoolForNextMonth(body); // do NOT retry within the month\n  }\n}","preventionTips":["Alert at 80% used/cap so teams act before the 402 wall.","Batch requests and disable generate where outputs are unneeded to cut counted usage.","Cache quota state between calls to avoid burning requests probing it."],"tags":["quota","http-402","billing","middleware"],"backgroundTag":"monthly-quota-exceeded","analyzedSha":"e2d1df569a8f04075d40e92461128ece7cf04c82","analyzedAt":"2026-08-20T23:58:13.836Z","contentChangedAt":"2026-08-20T23:58:13.836Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}