RocketChat/Rocket.Chat · error · MeteorError
error-role-protected
error-role-protected
Error message
Role is protected
What it means
Thrown by updateRole in updateRole.ts:24 when the role is marked protected AND the caller tries to change its name or scope. Protected roles (e.g. admin, user, bot, guest) cannot be renamed or rescoped. MeteorError code 'error-role-protected'.
Source
Thrown at apps/meteor/ee/server/lib/roles/updateRole.ts:24
import { notifyOnRoleChangedById } from '../../../../server/lib/notifyListener';
type UpdateRoleOptions = {
broadcastUpdate?: boolean;
};
export const updateRole = async (
roleId: IRole['_id'],
roleData: Omit<IRole, '_id' | '_updatedAt'>,
options: UpdateRoleOptions = {},
): Promise<IRole> => {
const role = await Roles.findOneById(roleId);
if (!role) {
throw new MeteorError('error-invalid-roleId', 'This role does not exist');
}
if (role.protected && ((roleData.name && roleData.name !== role.name) || (roleData.scope && roleData.scope !== role.scope))) {
throw new MeteorError('error-role-protected', 'Role is protected');
}
if (roleData.name) {
const otherRole = await Roles.findOneByName(roleData.name, { projection: { _id: 1 } });
if (otherRole && otherRole._id !== role._id) {
throw new MeteorError('error-duplicate-role-names-not-allowed', 'Role name already exists');
}
} else {
roleData.name = role.name;
}
if (roleData.scope) {
if (!isValidRoleScope(roleData.scope)) {
throw new MeteorError('error-invalid-scope', 'Invalid scope');
}
} else {
roleData.scope = role.scope;
}View on GitHub (pinned to f9d3ec372b)
Solutions
- Do not change name or scope on protected roles — edit only description/mandatory2fa.
- If a name/scope change is truly required, unset protected on the model first (deliberate, rare).
- Catch MeteorError 'error-role-protected' and inform the user the role is system-managed.
Example fix
// before
await updateRole(roleId, { name: newName, scope: newScope });
// after
if (role.protected && (newName !== role.name || newScope !== role.scope)) {
throw new Error('protected role: name/scope immutable');
}
await updateRole(roleId, { name: role.name, scope: role.scope, description, mandatory2fa }); Defensive patterns
Strategy: validation
Validate before calling
if (role.protected && ((roleData.name && roleData.name !== role.name) || (roleData.scope && roleData.scope !== role.scope))) {
throw new Error('protected role: name/scope immutable');
} Type guard
const isProtectedRole = (role: IRole): role is IRole & { protected: true } => Boolean(role.protected); Try / catch
try { await updateRole(roleId, roleData); }
catch (e) {
if (e?.code === 'error-role-protected') { /* inform user */ return; }
throw e;
} Prevention
- Hide/disable name and scope fields for protected roles in the UI.
- Never rename built-in roles in scripts.
When it happens
Trigger: Submitting an update for a protected role where roleData.name !== role.name or roleData.scope !== role.scope. Description and mandatory2fa edits are allowed.
Common situations: Admin UI pre-fills a protected role's form and the user changes the name field; migration script tries to normalize built-in role names.
Related errors
- error-duplicate-role-names-not-allowed
- error-invalid-scope
- error-invalid-roleId
- error-duplicate-role-names-not-allowed
- error-invalid-scope
AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12).
Data as JSON: /api/errors/3ba4ae44904d05c1.
Report an issue: GitHub.