{"record":{"id":"6dca132552069ba7","repo":"RocketChat/Rocket.Chat","slug":"error-not-allowed-6dca13","errorCode":null,"errorMessage":"error-not-allowed","messagePattern":"error-not-allowed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/api/v1/e2e.ts","lineNumber":451,"sourceCode":"\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},\n\t)\n\t.post(\n\t\t'e2e.setUserPublicAndPrivateKeys',\n\t\t{\n\t\t\tauthRequired: true,\n\t\t\tbody: ise2eSetUserPublicAndPrivateKeysParamsPOST,","sourceCodeStart":433,"sourceCodeEnd":469,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/api/v1/e2e.ts#L433-L469","documentation":"Thrown by POST e2e.resetRoomKey when the caller holds the toggle-room-e2e-encryption permission but still cannot access the room: after the permission check passes and the lock is taken, the endpoint runs canAccessRoomIdAsync(rid, this.userId) and throws new Error('error-not-allowed') when the user is not in the room or rid is invalid. Typical for admins with a global permission who are not members of the target room. Because it is thrown after LockMap.set(rid, true) and outside the try/finally, this path also leaves the room's reset lock stuck until server restart (every later call fails with error-e2e-key-reset-in-progress).","triggerScenarios":"An admin (global toggle-room-e2e-encryption) calling e2e.resetRoomKey for a room they never joined, or with a nonexistent rid; the room being deleted between requests.","commonSituations":"Ops scripts rotating keys for all encrypted rooms using one admin service account that has permission but no membership; typos in rid; targets that are DMs the admin is not part of.","solutions":["Run the reset as a user who is a member of the room (e.g. the room owner), or add the service account to the room first.","Verify rid with rooms.info?roomId=<rid> before resetting.","If you already hit this, note the room lock is now wedged in that server process — restart the Meteor app to clear LockMap, then retry as a member.","Track upstream: a server-side fix should move the access check before LockMap.set or wrap it in the finally cleanup."],"exampleFix":"// before (admin token, not a room member)\nawait api.post('e2e.resetRoomKey', { rid, e2eKey, e2eKeyId });\n\n// after — ensure the caller can access the room before resetting\nconst info = await api.get('rooms.info', { roomId: rid });\nif (!info.room) throw new Error('room not accessible');\nawait api.post('e2e.resetRoomKey', { rid, e2eKey, e2eKeyId });","handlingStrategy":"validation","validationCode":"async function resetE2EKeyAsMember(api, rid: string, e2eKey: string, e2eKeyId: string) {\n  try { await api.get('rooms.info', { roomId: rid }); }\n  catch { throw new Error(`caller cannot access room ${rid} — join it first`); }\n  return api.post('e2e.resetRoomKey', { rid, e2eKey, e2eKeyId });\n}","typeGuard":null,"tryCatchPattern":"try { await api.post('e2e.resetRoomKey', body); }\ncatch (e) {\n  if (e.message === 'error-not-allowed') { /* permission ok but no room membership — join room, then note server lock may be wedged */ } else throw e;\n}","preventionTips":["Run key resets as a room member (e.g. room owner), not a mere permission holder.","Verify rid via rooms.info before posting.","After hitting this, restart the Meteor server before retrying — the room's lock is leaked."],"tags":["rocketchat","rest-api","e2e-encryption","permissions","room-access","lock-leak"],"backgroundTag":"room-access-denied","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}