{"record":{"id":"c981f4f50ccbe914","repo":"Significant-Gravitas/AutoGPT","slug":"a-reset-is-already-in-progress-please-try-again","errorCode":null,"errorMessage":"A reset is already in progress. Please try again.","messagePattern":"A reset is already in progress\\. Please try again\\.","errorType":"http","errorClass":"HTTPException","httpStatus":429,"severity":"warning","filePath":"autogpt_platform/backend/backend/api/features/chat/routes.py","lineNumber":1068,"sourceCode":"\n    # Check max daily resets.  get_daily_reset_count returns None when Redis\n    # is unavailable; reject the reset in that case to prevent unlimited\n    # free resets when the counter store is down.\n    reset_count = await get_daily_reset_count(user_id)\n    if reset_count is None:\n        raise HTTPException(\n            status_code=503,\n            detail=\"Unable to verify reset eligibility — please try again later.\",\n        )\n    if config.max_daily_resets > 0 and reset_count >= config.max_daily_resets:\n        raise HTTPException(\n            status_code=429,\n            detail=f\"You've used all {config.max_daily_resets} resets for today.\",\n        )\n\n    # Acquire a per-user lock to prevent TOCTOU races (concurrent resets).\n    if not await acquire_reset_lock(user_id):\n        raise HTTPException(\n            status_code=429,\n            detail=\"A reset is already in progress. Please try again.\",\n        )\n\n    try:\n        # Verify the user is actually at or over their daily limit.\n        # (rate_limit_reset_cost intentionally omitted — this object is only\n        # used for limit checks, not returned to the client.)\n        usage_status = await get_usage_status(\n            user_id=user_id,\n            daily_cost_limit=daily_limit,\n            weekly_cost_limit=weekly_limit,\n            tier=tier,\n        )\n        if daily_limit > 0 and usage_status.daily.used < daily_limit:\n            raise HTTPException(\n                status_code=400,\n                detail=\"You have not reached your daily limit yet.\",","sourceCodeStart":1050,"sourceCodeEnd":1086,"githubUrl":"https://github.com/Significant-Gravitas/AutoGPT/blob/9c8bb5550f446ba5d3046b78896578742495b3cf/autogpt_platform/backend/backend/api/features/chat/routes.py#L1050-L1086","documentation":"HTTP 429 from POST /usage/reset (routes.py:1068). The reset flow is guarded by a per-user lock (acquire_reset_lock/release_reset_lock, released in the route's finally block) to prevent TOCTOU races where two concurrent requests both pass eligibility checks and double-charge credits. If the lock cannot be acquired because another reset is mid-flight for the same user, the second request gets 429 immediately.","triggerScenarios":"Two near-simultaneous POST /usage/reset calls for the same user_id — e.g. double-click on the reset button, a frontend retry racing the original request, or duplicate submits from parallel tabs.","commonSituations":"UI without button disabling during in-flight requests; aggressive client retry on slow networks; test suites firing concurrent resets.","solutions":["Retry once after a short delay — the lock is released in the finally block as soon as the first request finishes.","Fix the client: disable the submit button while the request is in flight and deduplicate (idempotency key or single-flight wrapper).","Treat this 429 as transient contention, not as 'limit reached' — do not decrement any client-side reset counter."],"exampleFix":"// before\n<button onClick={() => post('/chat/usage/reset')}>Reset</button>\n\n// after — single-flight the request\nconst [busy, setBusy] = useState(false);\n<button disabled={busy} onClick={async () => {\n  setBusy(true);\n  try { await post('/chat/usage/reset'); }\n  finally { setBusy(false); }\n}}>Reset</button>","handlingStrategy":"retry","validationCode":"// single-flight the reset button so a second request never races the first\nif (resetInFlight) return;\nresetInFlight = true;\ntry { await post('/chat/usage/reset'); } finally { resetInFlight = false; }","typeGuard":null,"tryCatchPattern":"try { await post('/chat/usage/reset'); } catch (e) {\n  if (e.status === 429 && /already in progress/.test(e.detail)) { await sleep(2_000); return retryOnce(); }\n  throw e;\n}","preventionTips":["Disable the submit button while a reset request is in flight","Use a single-flight/idempotency wrapper for paid mutations","Read the detail text: 'already in progress' is contention, not cap exhaustion"],"tags":["http","concurrency","locking","rate-limit","fastapi"],"backgroundTag":null,"analyzedSha":"9c8bb5550f446ba5d3046b78896578742495b3cf","analyzedAt":"2026-08-14T17:17:21.957Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}