{"record":{"id":"9616393730cab120","repo":"thedotmack/claude-mem","slug":"aborting-session-for-quota-guard-decision-reaso","errorCode":null,"errorMessage":"Aborting session for quota guard: ${decision.reason}","messagePattern":"Aborting session for quota guard: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/services/worker/ClaudeProvider.ts","lineNumber":319,"sourceCode":"          // session is out of usage too. set() dedupes: one event per\n          // exhausted window, not one per observer request against the wall.\n          if (globalRateLimitStore.set(info)) {\n            logger.warn('SDK', 'Subscription usage limit hit', {\n              sessionDbId: session.sessionDbId,\n              window: info.rateLimitType,\n              overageStatus: info.overageStatus,\n            });\n            captureEvent('usage_limit_hit', {\n              ...buildUsageLimitHitProps(info),\n              ide: session.platformSource,\n              provider: 'claude',\n              observed_model: session.observedModel,\n              observed_billing: session.observedBilling,\n            });\n          }\n          const decision = shouldAbortForQuota(authMethod, globalRateLimitStore);\n          if (decision.abort) {\n            logger.warn('SDK', `Aborting session for quota guard: ${decision.reason}`, {\n              sessionDbId: session.sessionDbId,\n              window: decision.window,\n              authMethod,\n            });\n            session.abortReason = `quota:${decision.window ?? 'unknown'}`;\n            try {\n              session.abortController.abort();\n            } catch {\n              // best-effort\n            }\n            break;\n          }\n        }\n\n        if (message.session_id && message.session_id !== session.memorySessionId) {\n          const previousId = session.memorySessionId;\n          session.memorySessionId = message.session_id;\n          this.dbManager.getSessionStore().ensureMemorySessionIdRegistered(","sourceCodeStart":301,"sourceCodeEnd":337,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/8bc631a71a487424b866756e43a6efa4574cc66b/src/services/worker/ClaudeProvider.ts#L301-L337","documentation":"When the SDK emits a rate_limit system message, claude-mem stores the snapshot and consults shouldAbortForQuota(). API-key auth never aborts (per-call billing is pre-authorized), but subscription/OAuth auth aborts when a window (five_hour, seven_day_opus, seven_day_sonnet, seven_day, overage) crosses its utilization threshold or the provider explicitly reports the bucket exhausted/rejected. This warning fires and the observer session is aborted via its AbortController.","triggerScenarios":"Subscription auth with the five-hour window near its utilization threshold; a seven-day Opus/Sonnet bucket reported as rejected; overage window exhausted; a reset due within the 15-minute grace buffer while utilization is above the floor.","commonSituations":"Heavy parallel Claude Code plus observer usage on a Pro/Max plan near quota; weekend sprints exhausting the five-hour window; the observer's background summarization tipping an already-high bucket.","solutions":["Wait for the rate-limit window to reset (the guard deliberately stops before burning the last few percent)","Switch the observer to API-key auth so per-call spend is authorized and the quota guard never aborts","Reduce consumption: lower observer frequency, close unneeded sessions, or free the busy window before resuming"],"exampleFix":"# switch observer to API-key auth so the subscription quota guard does not abort\n# ~/.claude-mem/.env\nANTHROPIC_AUTH_METHOD=api_key\nANTHROPIC_API_KEY=sk-ant-...","handlingStrategy":"validation","validationCode":"import { shouldAbortForQuota } from './RateLimitStore.js';\n\nfunction guardBeforeObserverRun(authMethod: string, store: RateLimitStore): void {\n  const decision = shouldAbortForQuota(authMethod, store);\n  if (decision.abort) {\n    throw new Error(`skipping observer run — ${decision.reason}`);\n  }\n}","typeGuard":"function isApiKeyAuth(authMethod: string): boolean {\n  return authMethod === 'api_key' || authMethod.startsWith('API key');\n}","tryCatchPattern":null,"preventionTips":["Check the rate-limit snapshot before launching observer sessions on subscription auth","Switch to API-key auth if the observer must run regardless of subscription windows","Respect the reset-grace window — retrying immediately after an abort just burns the remaining quota"],"tags":["rate-limit","quota","anthropic-api","observer","abort"],"backgroundTag":"rate-limit-exceeded","analyzedSha":"8bc631a71a487424b866756e43a6efa4574cc66b","analyzedAt":"2026-08-20T23:58:13.836Z","contentChangedAt":"2026-08-20T23:58:13.836Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}