{"record":{"id":"fb34557669bf61b0","repo":"RocketChat/Rocket.Chat","slug":"error-user-not-in-role","errorCode":"error-user-not-in-role","errorMessage":"User is not in this role","messagePattern":"User is not in this role","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"warning","filePath":"apps/meteor/server/api/v1/roles.ts","lineNumber":297,"sourceCode":"\n\t\t\tif (!roleId) {\n\t\t\t\treturn API.v1.failure('error-invalid-role-properties');\n\t\t\t}\n\n\t\t\tconst user = await Users.findOneByUsername(username);\n\n\t\t\tif (!user) {\n\t\t\t\tthrow new Meteor.Error('error-invalid-user', 'There is no user with this username');\n\t\t\t}\n\n\t\t\tconst role = await Roles.findOneById(roleId);\n\n\t\t\tif (!role) {\n\t\t\t\tthrow new Meteor.Error('error-invalid-roleId', 'This role does not exist');\n\t\t\t}\n\n\t\t\tif (!(await hasAnyRoleAsync(user._id, [role._id], scope))) {\n\t\t\t\tthrow new Meteor.Error('error-user-not-in-role', 'User is not in this role');\n\t\t\t}\n\n\t\t\tif (role._id === 'admin') {\n\t\t\t\tconst adminCount = await Roles.countUsersInRole('admin');\n\t\t\t\tif (adminCount === 1) {\n\t\t\t\t\tthrow new Meteor.Error('error-admin-required', 'You need to have at least one admin');\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait removeUserFromRolesAsync(user._id, [role._id], scope);\n\n\t\t\tif (settings.get('UI_DisplayRoles')) {\n\t\t\t\tvoid api.broadcast('user.roleUpdate', {\n\t\t\t\t\ttype: 'removed',\n\t\t\t\t\t_id: role._id,\n\t\t\t\t\tu: {\n\t\t\t\t\t\t_id: user._id,\n\t\t\t\t\t\tusername: user.username,","sourceCodeStart":279,"sourceCodeEnd":315,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/f9d3ec372bb580fa8d036f94cf03925a478ef768/apps/meteor/server/api/v1/roles.ts#L279-L315","documentation":"Thrown by POST roles.removeUserFromRole when hasAnyRoleAsync(user._id, [role._id], scope) is false. The user and role both exist, but the user does not currently hold the role in the given scope (or globally if scope omitted). Returns a structured Meteor.Error('error-user-not-in-role', ...).","triggerScenarios":"POST /api/v1/roles.removeUserFromRole for a user+role+scope combination the user does not have; re-running an unassign; scope mismatch (e.g. room-scoped vs global).","commonSituations":"Idempotent cleanup script re-removes roles; UI shows stale membership; role grant was room-scoped but removal sent without the matching scope.","solutions":["Treat the error as success for idempotent unassign workflows (user is already not in the role).","Match the scope exactly: if the grant was room-scoped, pass the same roomId/scope on removal.","Verify current membership via roles.getUsersInRole before removing."],"exampleFix":"// before\nawait fetch('/api/v1/roles.removeUserFromRole', { method:'POST', body: JSON.stringify({ roleId, username }) });\n\n// after - idempotent unassign\ntry {\n  await fetch('/api/v1/roles.removeUserFromRole', { method:'POST', body: JSON.stringify({ roleId, username }) });\n} catch (e) {\n  if (e.error === 'error-user-not-in-role') return; // already in desired state\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await fetch('/api/v1/roles.removeUserFromRole', {method:'POST',body:JSON.stringify({roleId,username,scope})}).then(r=>r.json());\n} catch (e) {\n  if (e.error === 'error-user-not-in-role') return; // idempotent success\n  throw e;\n}","preventionTips":["Match the original grant's scope when unassigning.","Treat not-in-role as the desired end state."],"tags":["roles","rest-api","conflict","idempotent","authorization"],"backgroundTag":null,"analyzedSha":"f9d3ec372bb580fa8d036f94cf03925a478ef768","analyzedAt":"2026-08-12T19:07:17.372Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}