{"record":{"id":"9b9f3b08a772d24d","repo":"RocketChat/Rocket.Chat","slug":"error-not-allowed-9b9f3b","errorCode":"error-not-allowed","errorMessage":"Not allowed","messagePattern":"Not allowed","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/meteor-methods/rooms/addRoomLeader.ts","lineNumber":26,"sourceCode":"import { hasPermissionAsync } from '../../lib/authorization/hasPermission';\nimport { methodDeprecationLogger } from '../../lib/deprecationWarningLogger';\nimport { notifyOnSubscriptionChangedById } from '../../lib/notifyListener';\nimport { syncRoomRolePriorityForUserAndRoom } from '../../lib/roles/syncRoomRolePriority';\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\taddRoomLeader(rid: IRoom['_id'], userId: IUser['_id']): boolean;\n\t}\n}\n\nexport const addRoomLeader = async (fromUserId: IUser['_id'], rid: IRoom['_id'], userId: IUser['_id']): Promise<boolean> => {\n\tcheck(rid, String);\n\tcheck(userId, String);\n\n\tif (!(await hasPermissionAsync(fromUserId, 'set-leader', rid))) {\n\t\tthrow new Meteor.Error('error-not-allowed', 'Not allowed', {\n\t\t\tmethod: 'addRoomLeader',\n\t\t});\n\t}\n\n\tconst user = await Users.findOneById(userId);\n\n\tif (!user?.username) {\n\t\tthrow new Meteor.Error('error-invalid-user', 'Invalid user', {\n\t\t\tmethod: 'addRoomLeader',\n\t\t});\n\t}\n\n\tconst subscription = await Subscriptions.findOneByRoomIdAndUserId(rid, user._id);\n\n\tif (!subscription) {\n\t\tthrow new Meteor.Error('error-user-not-in-room', 'User is not in this room', {\n\t\t\tmethod: 'addRoomLeader',\n\t\t});","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/meteor-methods/rooms/addRoomLeader.ts#L8-L44","documentation":"addRoomLeader requires the caller to hold the 'set-leader' permission scoped to the room (hasPermissionAsync(fromUserId, 'set-leader', rid)). If the caller's roles lack it globally and for that room, the method throws error-not-allowed before the target user is even loaded. The method is deprecated since 9.0.0 in favor of /v1/channels.addLeader and /v1/groups.addLeader, which enforce the same permission.","triggerScenarios":"A non-privileged user calling addRoomLeader, or REST channels.addLeader/groups.addLeader with a token whose user lacks set-leader for that channel/team room.","commonSituations":"Owners assuming channel-owner rights include set-leader; room-scoped permission overrides removed by admins; custom clients calling the legacy method with a member account.","solutions":["Grant set-leader to the caller's role globally or for that specific room in Administration > Permissions.","Use the REST endpoints with a user/token that holds set-leader.","Catch error-not-allowed and disable the 'Set as leader' UI action for users without the permission."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// hide 'Set as leader' unless the caller holds set-leader for the room\nif (!(await hasPermission('set-leader', rid))) {\n\t// do not offer the action\n}","typeGuard":null,"tryCatchPattern":"try {\n\tawait Meteor.callAsync('addRoomLeader', rid, userId);\n} catch (e: any) {\n\tif (e?.error === 'error-not-allowed') {\n\t\t// caller lacks set-leader: surface authorization error, no retry\n\t}\n}","preventionTips":["Check set-leader (global and room scope) before exposing the action.","Use elevated/admin tokens for role-change automation.","Remember channel ownership alone does not imply set-leader."],"tags":["meteor-methods","authorization","rooms","roles"],"backgroundTag":"permission-denied","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}