RocketChat/Rocket.Chat · warning · Meteor.Error
error-role-in-use
error-role-in-use
Error message
Cannot delete role because it's in use
What it means
Thrown by POST roles.delete when Roles.countUsersInRole(role._id) is greater than zero. The role is non-protected but still has users assigned, so deletion is blocked to avoid orphaned assignments. Returns a structured Meteor.Error('error-role-in-use', ...).
Source
Thrown at apps/meteor/server/api/v1/roles.ts:247
401: validateUnauthorizedErrorResponse,
403: validateForbiddenErrorResponse,
},
},
async function action() {
const { bodyParams } = this;
const role = await Roles.findOneByIdOrName(bodyParams.roleId);
if (!role) {
throw new Meteor.Error('error-invalid-roleId', 'This role does not exist');
}
if (role.protected) {
throw new Meteor.Error('error-role-protected', 'Cannot delete a protected role');
}
if ((await Roles.countUsersInRole(role._id)) > 0) {
throw new Meteor.Error('error-role-in-use', "Cannot delete role because it's in use");
}
await Roles.removeById(role._id);
void notifyOnRoleChanged(role, 'removed');
return API.v1.success();
},
)
.post(
'roles.removeUserFromRole',
{
authRequired: true,
permissionsRequired: ['access-permissions'],
body: isRoleRemoveUserFromRoleProps,
response: {
200: ajv.compile<{ role: IRole }>({
type: 'object',View on GitHub (pinned to f9d3ec372b)
Solutions
- Remove all users from the role first via roles.removeUserFromRole (per user/scope), then retry the delete.
- Use GET /api/v1/roles.getUsersInRole to enumerate current grantees before unassigning.
- Confirm count is truly zero with countUsersInRole before the delete call.
Example fix
// before
await fetch('/api/v1/roles.delete', { method:'POST', body: JSON.stringify({ roleId }) });
// after - unassign all users, then delete
const { users } = await fetch(`/api/v1/roles.getUsersInRole?role=${roleId}`).then(r=>r.json());
for (const u of users) {
await fetch('/api/v1/roles.removeUserFromRole', { method:'POST', body: JSON.stringify({ roleId, username: u.username }) });
}
await fetch('/api/v1/roles.delete', { method:'POST', body: JSON.stringify({ roleId }) }); Defensive patterns
Strategy: validation
Validate before calling
// Ensure the role has no grantees before deleting
const { total } = await fetch(`/api/v1/roles.getUsersInRole?role=${encodeURIComponent(roleId)}`).then(r => r.json());
if (total > 0) throw new Error(`Role still has ${total} user(s); unassign first`);
await fetch('/api/v1/roles.delete', { method:'POST', body: JSON.stringify({ roleId }) }); Try / catch
try {
await fetch('/api/v1/roles.delete', {method:'POST',body:JSON.stringify({roleId})}).then(r=>r.json());
} catch (e) {
if (e.error === 'error-role-in-use') { /* enumerate + unassign, then retry */ }
} Prevention
- Always unassign all users before deleting a role.
- Build a single 'purge role' routine that unassigns then deletes.
When it happens
Trigger: POST /api/v1/roles.delete for a non-protected role that still has at least one user granted it (globally or in any scope).
Common situations: Trying to clean up a custom role before removing all grantees; bulk permission refactor that forgets the unassign step.
Related errors
- error-role-protected
- error-user-already-in-role
- error-user-not-in-role
- error-admin-required
- error-action-not-allowed
AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12).
Data as JSON: /api/errors/c4898f7d362d5c96.
Report an issue: GitHub.