{"record":{"id":"59076c346931c2e5","repo":"RocketChat/Rocket.Chat","slug":"error-e2e-key-reset-in-progress","errorCode":"error-e2e-key-reset-in-progress","errorMessage":"error-e2e-key-reset-in-progress","messagePattern":"error-e2e-key-reset-in-progress","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"apps/meteor/server/api/v1/e2e.ts","lineNumber":445,"sourceCode":"\t\t\tauthRequired: true,\n\t\t\tbody: isE2EResetRoomKeyProps,\n\t\t\tresponse: {\n\t\t\t\t400: validateBadRequestErrorResponse,\n\t\t\t\t401: validateUnauthorizedErrorResponse,\n\t\t\t\t403: validateForbiddenErrorResponse,\n\t\t\t\t200: ajv.compile<void>({\n\t\t\t\t\ttype: 'object',\n\t\t\t\t}),\n\t\t\t},\n\t\t},\n\n\t\tasync function action() {\n\t\t\tconst { rid, e2eKey, e2eKeyId } = this.bodyParams;\n\t\t\tif (!(await hasPermissionAsync(this.user, 'toggle-room-e2e-encryption', rid))) {\n\t\t\t\treturn API.v1.forbidden('error-not-allowed');\n\t\t\t}\n\t\t\tif (LockMap.has(rid)) {\n\t\t\t\tthrow new Error('error-e2e-key-reset-in-progress');\n\t\t\t}\n\n\t\t\tLockMap.set(rid, true);\n\n\t\t\tif (!(await canAccessRoomIdAsync(rid, this.userId))) {\n\t\t\t\tthrow new Error('error-not-allowed');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tawait resetRoomKey(rid, this.userId, e2eKey, e2eKeyId);\n\t\t\t\treturn API.v1.success();\n\t\t\t} catch (e) {\n\t\t\t\tconsole.error(e);\n\t\t\t\treturn API.v1.failure('error-e2e-key-reset-failed');\n\t\t\t} finally {\n\t\t\t\tLockMap.delete(rid);\n\t\t\t}\n\t\t},","sourceCodeStart":427,"sourceCodeEnd":463,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/f9d3ec372bb580fa8d036f94cf03925a478ef768/apps/meteor/server/api/v1/e2e.ts#L427-L463","documentation":"Thrown by POST e2e.resetRoomKey when LockMap already has an entry for rid — another resetRoomKey request for the same room is in flight on this server. It is an in-memory per-server concurrency lock. It is transient and thrown as a plain Error (not Meteor.Error). IMPORTANT: a related defect in this same handler (see error 389) can leak the lock, making this error permanent for an affected room until restart.","triggerScenarios":"User double-clicks reset; client auto-retries; two admins reset the same room key at once; a prior request that failed the access check left the lock set (lock-leak bug).","commonSituations":"Impatient double-submit; network retry storm; prior access-denied attempt poisoned the room (see 389).","solutions":["Wait for the in-flight reset to finish, then retry once.","Debounce/disable the reset button client-side until the response arrives.","If the error persists indefinitely, restart the server process to clear a leaked lock, and apply the fix in error 389."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// track in-flight resets client-side per room\nif (inFlightResets.has(rid)) { /* wait or skip */ }","typeGuard":null,"tryCatchPattern":"for (const delay of [500, 1000, 2000]) {\n  try { return await resetRoomKey({ rid, e2eKey, e2eKeyId }); }\n  catch (e) {\n    if (e?.message === 'error-e2e-key-reset-in-progress') { await sleep(delay); continue; }\n    throw e;\n  }\n}\nthrow new Error('room key reset stayed locked');","preventionTips":["Debounce the reset button so only one request per room is in flight.","If the error persists across retries, suspect the lock-leak defect (error 389) and restart the server.","Avoid issuing resetRoomKey from multiple sessions simultaneously."],"tags":["e2e","concurrency","lock","rest-api"],"backgroundTag":null,"analyzedSha":"f9d3ec372bb580fa8d036f94cf03925a478ef768","analyzedAt":"2026-08-12T19:07:17.372Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}