{"record":{"id":"8998fa62136cd9cf","repo":"RocketChat/Rocket.Chat","slug":"error-room-e2e-key-already-exists","errorCode":"error-room-e2e-key-already-exists","errorMessage":"E2E Key ID already exists","messagePattern":"E2E Key ID already exists","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/meteor-methods/platform/setRoomKeyID.ts","lineNumber":25,"sourceCode":"import { canAccessRoomIdAsync } from '../../lib/authorization/canAccessRoom';\nimport { notifyOnRoomChanged } from '../../lib/notifyListener';\n\ndeclare module '@rocket.chat/ddp-client' {\n\t// eslint-disable-next-line @typescript-eslint/naming-convention\n\tinterface ServerMethods {\n\t\t'e2e.setRoomKeyID'(rid: IRoom['_id'], keyID: string): void;\n\t}\n}\n\nexport const setRoomKeyIDMethod = async (userId: string, rid: IRoom['_id'], keyID: string): Promise<void> => {\n\tif (!(await canAccessRoomIdAsync(rid, userId))) {\n\t\tthrow new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'e2e.setRoomKeyID' });\n\t}\n\n\tconst room = await Rooms.setE2eKeyId(rid, keyID);\n\n\tif (!room) {\n\t\tthrow new Meteor.Error('error-room-e2e-key-already-exists', 'E2E Key ID already exists', {\n\t\t\tmethod: 'e2e.setRoomKeyID',\n\t\t});\n\t}\n\n\tvoid notifyOnRoomChanged(room);\n};\n\nMeteor.methods<ServerMethods>({\n\tasync 'e2e.setRoomKeyID'(rid, keyID) {\n\t\tcheck(rid, String);\n\t\tcheck(keyID, String);\n\n\t\tconst userId = Meteor.userId();\n\t\tif (!userId) {\n\t\t\tthrow new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'e2e.setRoomKeyID' });\n\t\t}\n\n\t\tif (!rid) {","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/meteor-methods/platform/setRoomKeyID.ts#L7-L43","documentation":"After the access check passes, `Rooms.setE2eKeyId(rid, keyID)` returning null makes the method throw `error-room-e2e-key-already-exists`: the model layer refuses to overwrite an existing `e2eKeyId`. Through this method a room's E2E key id is write-once; a second submission with a different key is rejected.","triggerScenarios":"Calling `e2e.setRoomKeyID` twice for the same room — duplicate submission (double click), a retry after a timeout where the first call actually committed, or an attempted key rotation without going through a reset flow.","commonSituations":"Non-idempotent E2E setup wizards re-running on reconnect; retry logic resending the key; two clients racing to set the room key; network slowness making the user resubmit.","solutions":["Read the room's `e2eKeyId` (rooms subscription) first and skip the call when it is already set","Make the client's key submission idempotent (guard with a pending/submitted flag)","For genuine key rotation, use the designated reset flow (e.g. `e2e.resetOwnE2EKey` / admin reset) instead of calling setRoomKeyID again"],"exampleFix":"// before\nMeteor.call('e2e.setRoomKeyID', rid, keyID);\n// after\nconst room = Rooms.findOne({ _id: rid });\nif (room?.e2eKeyId) {\n  return; // key already established, nothing to do\n}\nMeteor.call('e2e.setRoomKeyID', rid, keyID);","handlingStrategy":"validation","validationCode":"const room = Rooms.findOne({ _id: rid });\nif (room?.e2eKeyId) {\n  return; // already established — skip the call entirely\n}\nMeteor.call('e2e.setRoomKeyID', rid, keyID);","typeGuard":"const isRoomKeyUnset = (room: IRoom | undefined | null): room is IRoom =>\n  !!room && !room.e2eKeyId;","tryCatchPattern":"try {\n  await Meteor.callAsync('e2e.setRoomKeyID', rid, keyID);\n} catch (e) {\n  if (e instanceof Meteor.Error && e.error === 'error-room-e2e-key-already-exists') {\n    // treat as success (key already set) or re-read the room key and adopt it\n  }\n}","preventionTips":["Read room.e2eKeyId before submitting — the field is write-once","Make the E2E handshake idempotent; guard against double submission","Use the dedicated reset flow for rotation instead of re-calling setRoomKeyID"],"tags":["e2ee","rooms","idempotency","meteor-method"],"backgroundTag":"e2e-key-already-exists","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}