{"record":{"id":"6b0eb8cec1a9c55e","repo":"RocketChat/Rocket.Chat","slug":"error-invalid-room-6b0eb8","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/rooms/addRoomModerator.ts","lineNumber":30,"sourceCode":"import { notifyOnSubscriptionChangedById } from '../../lib/notifyListener';\nimport { syncRoomRolePriorityForUserAndRoom } from '../../lib/roles/syncRoomRolePriority';\nimport { isFederationEnabled, FederationMatrixInvalidConfigurationError } from '../../services/federation/utils';\nimport { settings } from '../../settings';\n\ndeclare module '@rocket.chat/ddp-client' {\n\t// eslint-disable-next-line @typescript-eslint/naming-convention\n\tinterface ServerMethods {\n\t\taddRoomModerator(rid: IRoom['_id'], userId: IUser['_id']): boolean;\n\t}\n}\n\nexport const addRoomModerator = async (fromUserId: IUser['_id'], rid: IRoom['_id'], userId: IUser['_id']): Promise<boolean> => {\n\tcheck(rid, String);\n\tcheck(userId, String);\n\n\tconst room = await Rooms.findOneById(rid, { projection: { t: 1, federated: 1, federation: 1 } });\n\tif (!room) {\n\t\tthrow new Meteor.Error('error-invalid-room', 'Invalid room', {\n\t\t\tmethod: 'addRoomModerator',\n\t\t});\n\t}\n\n\tconst isFederated = isRoomFederated(room);\n\n\tif (!(await hasPermissionAsync(fromUserId, 'set-moderator', rid)) && !isFederated) {\n\t\tthrow new Meteor.Error('error-not-allowed', 'Not allowed', {\n\t\t\tmethod: 'addRoomModerator',\n\t\t});\n\t}\n\n\tif (isFederated && !isFederationEnabled()) {\n\t\tthrow new FederationMatrixInvalidConfigurationError('unable to change room owners');\n\t}\n\n\tconst user = await Users.findOneById(userId);\n","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/meteor-methods/rooms/addRoomModerator.ts#L12-L48","documentation":"addRoomModerator begins by loading the room with Rooms.findOneById (projection t/federated/federation); a null result throws error-invalid-room. The room lookup precedes both the set-moderator permission check and the federation checks, so a bad rid fails before anything else. Note addRoomModerator validates the room first, unlike addRoomLeader which checks permission first.","triggerScenarios":"Passing a nonexistent or deleted rid; the room was removed between the client loading it and the role-change call; typos in scripted rids.","commonSituations":"Stale room selectors; deletion races during channel cleanup; scripts hardcoding ids from an old export.","solutions":["Verify the room exists (GET /v1/channels.info or /v1/groups.info) before calling.","Catch error-invalid-room and refresh the room data the UI operates on.","Resolve rid from the room name at call time in automation."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// validate the room id before the role change\nconst room = await fetch(`/api/v1/rooms.info?roomId=${rid}`, { headers }).then((r) => r.json());\nif (!room.success) {\n\tthrow new Error(`Room ${rid} not found`);\n}","typeGuard":null,"tryCatchPattern":"try {\n\tawait Meteor.callAsync('addRoomModerator', rid, userId);\n} catch (e: any) {\n\tif (e?.error === 'error-invalid-room') {\n\t\t// stale rid: refresh room data, re-run with the current id\n\t}\n}","preventionTips":["Resolve rids at call time; never hardcode them in scripts.","Invalidate cached room references on room-deleted events.","Validate the rid format before any room-scoped call."],"tags":["meteor-methods","rooms","not-found","roles"],"backgroundTag":"room-not-found","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}