{"record":{"id":"edebc7cbc763a918","repo":"RocketChat/Rocket.Chat","slug":"error-not-allowed-edebc7","errorCode":"error-not-allowed","errorMessage":"Not allowed","messagePattern":"Not allowed","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/api/v1/roles.ts","lineNumber":193,"sourceCode":"\t\t},\n\t\tasync function action() {\n\t\t\tconst { roomId, role } = this.queryParams;\n\t\t\tconst { offset, count = 50 } = await getPaginationItems(this.queryParams);\n\n\t\t\tconst projection = {\n\t\t\t\tname: 1,\n\t\t\t\tusername: 1,\n\t\t\t\temails: 1,\n\t\t\t\tavatarETag: 1,\n\t\t\t\tcreatedAt: 1,\n\t\t\t\t_updatedAt: 1,\n\t\t\t};\n\n\t\t\tif (!role) {\n\t\t\t\tthrow new Meteor.Error('error-param-not-provided', 'Query param \"role\" is required');\n\t\t\t}\n\t\t\tif (roomId && !(await hasPermissionAsync(this.user, 'view-other-user-channels'))) {\n\t\t\t\tthrow new Meteor.Error('error-not-allowed', 'Not allowed');\n\t\t\t}\n\n\t\t\tconst options = { projection: { _id: 1 } };\n\t\t\tconst roleData = await Roles.findOneById<Pick<IRole, '_id'>>(role, options);\n\n\t\t\tif (!roleData) {\n\t\t\t\tthrow new Meteor.Error('error-invalid-roleId');\n\t\t\t}\n\n\t\t\tconst { cursor, totalCount } = await getUsersInRolePaginated(roleData._id, roomId, {\n\t\t\t\tlimit: count,\n\t\t\t\tsort: { username: 1 },\n\t\t\t\tskip: offset,\n\t\t\t\tprojection,\n\t\t\t});\n\n\t\t\tconst [users, total] = await Promise.all([cursor.toArray(), totalCount]);\n","sourceCodeStart":175,"sourceCodeEnd":211,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/f9d3ec372bb580fa8d036f94cf03925a478ef768/apps/meteor/server/api/v1/roles.ts#L175-L211","documentation":"Thrown by GET roles.getUsersInRole when a roomId query param is supplied AND the authenticated user lacks the 'view-other-user-channels' permission. The route already requires 'access-permissions'; this is an additional guard for room-scoped user listing. Returns a structured Meteor.Error('error-not-allowed', ...).","triggerScenarios":"GET /api/v1/roles.getUsersInRole?role=<r>&roomId=<room> by a user who has 'access-permissions' but not 'view-other-user-channels'.","commonSituations":"A moderator/admin with limited permissions tries to list role members scoped to a room they cannot view; permission set was tightened; custom role with partial grants.","solutions":["Drop the roomId param to list role members workspace-wide instead of scoped to a room.","Grant the user 'view-other-user-channels' if room-scoped listing is legitimately required.","Request the listing from an account that holds both permissions."],"exampleFix":"// before\nfetch(`/api/v1/roles.getUsersInRole?role=${role}&roomId=${roomId}`);\n\n// after - omit roomId when caller lacks view-other-user-channels\nfetch(`/api/v1/roles.getUsersInRole?role=${role}`);","handlingStrategy":"validation","validationCode":"// If the caller may lack view-other-user-channels, omit roomId\nfunction buildUsersInRoleUrl(role: string, roomId?: string, canViewOtherChannels?: boolean): string {\n  const base = `/api/v1/roles.getUsersInRole?role=${encodeURIComponent(role)}`;\n  return roomId && canViewOtherChannels ? `${base}&roomId=${encodeURIComponent(roomId)}` : base;\n}","typeGuard":null,"tryCatchPattern":"try {\n  await fetch(url).then(r => r.json());\n} catch (e) {\n  if (e.error === 'error-not-allowed') { /* retry without roomId */ }\n}","preventionTips":["Only scope by roomId when the caller is known to hold view-other-user-channels.","Fall back to a workspace-wide listing on this error."],"tags":["roles","rest-api","authorization","permissions","forbidden"],"backgroundTag":null,"analyzedSha":"f9d3ec372bb580fa8d036f94cf03925a478ef768","analyzedAt":"2026-08-12T19:07:17.372Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}