RocketChat/Rocket.Chat · warning · Meteor.Error
error-user-not-in-role
error-user-not-in-role
Error message
User is not in this role
What it means
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', ...).
Source
Thrown at apps/meteor/server/api/v1/roles.ts:297
if (!roleId) {
return API.v1.failure('error-invalid-role-properties');
}
const user = await Users.findOneByUsername(username);
if (!user) {
throw new Meteor.Error('error-invalid-user', 'There is no user with this username');
}
const role = await Roles.findOneById(roleId);
if (!role) {
throw new Meteor.Error('error-invalid-roleId', 'This role does not exist');
}
if (!(await hasAnyRoleAsync(user._id, [role._id], scope))) {
throw new Meteor.Error('error-user-not-in-role', 'User is not in this role');
}
if (role._id === 'admin') {
const adminCount = await Roles.countUsersInRole('admin');
if (adminCount === 1) {
throw new Meteor.Error('error-admin-required', 'You need to have at least one admin');
}
}
await removeUserFromRolesAsync(user._id, [role._id], scope);
if (settings.get('UI_DisplayRoles')) {
void api.broadcast('user.roleUpdate', {
type: 'removed',
_id: role._id,
u: {
_id: user._id,
username: user.username,View on GitHub (pinned to f9d3ec372b)
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.
Example fix
// before
await fetch('/api/v1/roles.removeUserFromRole', { method:'POST', body: JSON.stringify({ roleId, username }) });
// after - idempotent unassign
try {
await fetch('/api/v1/roles.removeUserFromRole', { method:'POST', body: JSON.stringify({ roleId, username }) });
} catch (e) {
if (e.error === 'error-user-not-in-role') return; // already in desired state
throw e;
} Defensive patterns
Strategy: try-catch
Try / catch
try {
await fetch('/api/v1/roles.removeUserFromRole', {method:'POST',body:JSON.stringify({roleId,username,scope})}).then(r=>r.json());
} catch (e) {
if (e.error === 'error-user-not-in-role') return; // idempotent success
throw e;
} Prevention
- Match the original grant's scope when unassigning.
- Treat not-in-role as the desired end state.
When it happens
Trigger: 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).
Common situations: Idempotent cleanup script re-removes roles; UI shows stale membership; role grant was room-scoped but removal sent without the matching scope.
Related errors
- error-user-already-in-role
- error-not-allowed
- error-role-in-use
- error-not-authorized
- error-action-not-allowed
AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12).
Data as JSON: /api/errors/fb34557669bf61b0.
Report an issue: GitHub.