{"record":{"id":"b185a6b7878f58aa","repo":"RocketChat/Rocket.Chat","slug":"error-invalid-room-b185a6","errorCode":"error-invalid-room","errorMessage":"Invalid room","messagePattern":"Invalid room","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/ee/server/meteor-methods/getReadReceipts.ts","lineNumber":33,"sourceCode":"\t\tgetReadReceipts(options: { messageId: IMessage['_id'] }): IReadReceiptWithUser[];\n\t}\n}\n\nexport const getReadReceiptsFunction = async function (messageId: IMessage['_id'], userId: string): Promise<IReadReceiptWithUser[]> {\n\tif (!License.hasModule('message-read-receipt')) {\n\t\tthrow new Meteor.Error('error-action-not-allowed', 'This is an enterprise feature', { method: 'getReadReceipts' });\n\t}\n\tcheck(messageId, String);\n\n\tconst message = await Messages.findOneById(messageId, { projection: { _id: 1, rid: 1, receiptsArchived: 1 } });\n\tif (!message) {\n\t\tthrow new Meteor.Error('error-invalid-message', 'Invalid message', {\n\t\t\tmethod: 'getReadReceipts',\n\t\t});\n\t}\n\n\tif (!(await canAccessRoomIdAsync(message.rid, userId))) {\n\t\tthrow new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'getReadReceipts' });\n\t}\n\n\treturn ReadReceipt.getReceipts(message);\n};\n\nMeteor.methods<ServerMethods>({\n\tasync getReadReceipts({ messageId }) {\n\t\tmethodDeprecationLogger.method('getReadReceipts', '9.0.0', '/v1/chat.getMessageReadReceipts');\n\n\t\tcheck(messageId, String);\n\n\t\tconst uid = Meteor.userId();\n\t\tif (!uid) {\n\t\t\tthrow new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'getReadReceipts' });\n\t\t}\n\n\t\treturn getReadReceiptsFunction(messageId, uid);\n\t},","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/f9d3ec372bb580fa8d036f94cf03925a478ef768/apps/meteor/ee/server/meteor-methods/getReadReceipts.ts#L15-L51","documentation":"Thrown by getReadReceiptsFunction when canAccessRoomIdAsync(message.rid, userId) returns false — the calling user is not a member/authorized viewer of the room the message belongs to. Prevents cross-room receipt enumeration. Code is 'error-invalid-room'.","triggerScenarios":"A user calls getReadReceipts for a message in a room they cannot access (not a member, banned, different department, private channel they were removed from).","commonSituations":"User removed from a private room but still has cached message ids; cross-tenant/department data leakage attempt; token/anonymous user hitting a private message.","solutions":["Verify room membership/permission via canAccessRoomIdAsync before exposing the read-receipt action.","In the UI, only show 'who read this' for rooms the user currently belongs to.","Treat the error as an authorization failure, not a missing-room error."],"exampleFix":"// before\nconst receipts = await getReadReceiptsFunction(messageId, uid);\n\n// after\nif (!(await canAccessRoomIdAsync(message.rid, uid))) {\n  throw new Error('Not authorized to view this room');\n}\nconst receipts = await getReadReceiptsFunction(messageId, uid);","handlingStrategy":"validation","validationCode":"if (!(await canAccessRoomIdAsync(message.rid, uid))) {\n  throw new Error('Not authorized to view this room');\n}\nconst receipts = await getReadReceiptsFunction(messageId, uid);","typeGuard":null,"tryCatchPattern":"try {\n  await getReadReceiptsFunction(messageId, uid);\n} catch (e) {\n  if (isMeteorError(e, 'error-invalid-room')) {\n    notifyUser('You do not have access to this room.');\n    return;\n  }\n  throw e;\n}","preventionTips":["Verify room access before exposing read-receipt details.","Only show the action for rooms the user currently belongs to.","Treat this as authorization, not a missing-room condition."],"tags":["read-receipts","authorization","room-access"],"backgroundTag":null,"analyzedSha":"f9d3ec372bb580fa8d036f94cf03925a478ef768","analyzedAt":"2026-08-12T19:07:17.372Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}