RocketChat/Rocket.Chat · warning · Meteor.Error
error-role-protected
error-role-protected
Error message
Cannot delete a protected role
What it means
Thrown by POST roles.delete when the resolved role has role.protected === true. Protected roles (e.g. admin, owner, moderator, user) are built-in and cannot be removed. Returns a structured Meteor.Error('error-role-protected', 'Cannot delete a protected role').
Source
Thrown at apps/meteor/server/api/v1/roles.ts:243
required: ['success'],
additionalProperties: false,
}),
400: validateBadRequestErrorResponse,
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'],View on GitHub (pinned to f9d3ec372b)
Solutions
- Do not delete protected roles; instead remove users from the role or adjust permissions if you need to change behavior.
- Filter the role list by protected===false before offering a delete action in the UI.
- Create a custom non-protected role if you need a deletable equivalent.
Example fix
// before
await fetch('/api/v1/roles.delete', { method:'POST', body: JSON.stringify({ roleId }) });
// after - block protected roles in the UI
const role = roles.find(r => r._id === roleId);
if (role?.protected) {
alert('Protected roles cannot be deleted');
return;
}
await fetch('/api/v1/roles.delete', { method:'POST', body: JSON.stringify({ roleId }) }); Defensive patterns
Strategy: type-guard
Validate before calling
const role = roles.find(r => r._id === roleId);
if (!role || role.protected) {
throw new Error('Protected or unknown roles cannot be deleted');
}
await fetch('/api/v1/roles.delete', { method:'POST', body: JSON.stringify({ roleId }) }); Type guard
function isDeletableRole(role: { protected?: boolean } | undefined): role is { protected: false } {
return !!role && role.protected !== true;
} Prevention
- Filter protected roles out of the deletable list in the UI.
- Never script deletion of built-in system roles.
When it happens
Trigger: POST /api/v1/roles.delete targeting a built-in protected role whose documents has protected:true.
Common situations: Admin UI/script attempts to remove a system role to simplify permissions; migration tries to drop the 'admin' or 'user' role.
Related errors
- error-role-in-use
- error-admin-required
- error-action-not-allowed
- error-duplicate-role-names-not-allowed
- error-role-protected
AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12).
Data as JSON: /api/errors/8454985b0ec9460c.
Report an issue: GitHub.