{"record":{"id":"60d00bd461ba57f9","repo":"RocketChat/Rocket.Chat","slug":"error-invalid-room-60d00b","errorCode":"error-invalid-room","errorMessage":"Invalid room","messagePattern":"Invalid room","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/meteor-methods/platform/getUsersOfRoomWithoutKey.ts","lineNumber":21,"sourceCode":"import { Subscriptions, Users } from '@rocket.chat/models';\nimport { check } from 'meteor/check';\nimport { Meteor } from 'meteor/meteor';\n\nimport { canAccessRoomIdAsync } from '../../lib/authorization/canAccessRoom';\n\ndeclare module '@rocket.chat/ddp-client' {\n\t// eslint-disable-next-line @typescript-eslint/naming-convention\n\tinterface ServerMethods {\n\t\t'e2e.getUsersOfRoomWithoutKey'(rid: IRoom['_id']): { users: Pick<IUser, '_id' | 'e2e'>[] };\n\t}\n}\n\nexport const getUsersOfRoomWithoutKeyMethod = async (\n\tuserId: string,\n\trid: IRoom['_id'],\n): Promise<{ users: Pick<IUser, '_id' | 'e2e'>[] }> => {\n\tif (!(await canAccessRoomIdAsync(rid, userId))) {\n\t\tthrow new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'e2e.getUsersOfRoomWithoutKey' });\n\t}\n\n\tconst subscriptions = await Subscriptions.findByRidWithoutE2EKey(rid, {\n\t\tprojection: { 'u._id': 1 },\n\t}).toArray();\n\tconst userIds = subscriptions.map((s) => s.u._id);\n\tconst options = { projection: { 'e2e.public_key': 1 } };\n\n\tconst users = await Users.findByIdsWithPublicE2EKey(userIds, options).toArray();\n\n\treturn {\n\t\tusers,\n\t};\n};\n\nMeteor.methods<ServerMethods>({\n\tasync 'e2e.getUsersOfRoomWithoutKey'(rid) {\n\t\tcheck(rid, String);","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/meteor-methods/platform/getUsersOfRoomWithoutKey.ts#L3-L39","documentation":"Thrown by the exported helper getUsersOfRoomWithoutKeyMethod when canAccessRoomIdAsync(rid, userId) returns false — the room id does not exist or the user cannot access it (not a member, removed, or lacking view access). The guard runs before room members' E2E public keys are fetched, so membership information is never disclosed to unauthorized callers.","triggerScenarios":"Calling 'e2e.getUsersOfRoomWithoutKey' with a rid that was deleted; the caller was removed from the encrypted room mid key-handshake; the caller never joined the room and attempts to enumerate member public keys.","commonSituations":"Clients holding a stale cached rid after room deletion; E2E key exchange continuing after the user was kicked; probing private rooms with guessed ids.","solutions":["Verify the caller has a live subscription to rid before calling","Re-resolve rid from the user's current subscriptions instead of cached state","If membership was revoked, cancel the E2E key exchange flow for that room","On error, clear local E2E room-key state instead of retrying"],"exampleFix":"// before\nconst { users } = await Meteor.callAsync('e2e.getUsersOfRoomWithoutKey', rid);\n\n// after\nif (!rid || !hasSubscriptionFor(rid)) {\n  // user is not (or no longer) in this room; drop stale E2E state\n  return dropLocalRoomKey(rid);\n}\nconst { users } = await Meteor.callAsync('e2e.getUsersOfRoomWithoutKey', rid);","handlingStrategy":"validation","validationCode":"// client: confirm the caller belongs to the room before asking for member keys\n// (miningo subscription cache on the client)\nconst subscription = chatSubscriptionCollection.findOne({ rid });\nif (!subscription) {\n  // no membership (or room deleted): skip the method call\n}","typeGuard":"const isInvalidRoomError = (err: unknown): err is Meteor.Error =>\n  err instanceof Meteor.Error && err.error === 'error-invalid-room';","tryCatchPattern":"try {\n  const { users } = await Meteor.callAsync('e2e.getUsersOfRoomWithoutKey', rid);\n} catch (err) {\n  if (isInvalidRoomError(err)) {\n    // drop stale local E2E key state for this room; do not retry\n  } else {\n    throw err;\n  }\n}","preventionTips":["Derive rid from the user's live subscription rather than cached state","Abort E2E key handshakes when room membership is lost","Treat this error as a signal to clean up local room-key state, not to retry"],"tags":["meteor","authorization","rocket-chat","e2e-encryption","rooms"],"backgroundTag":"room-access-denied","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}