{"record":{"id":"89989edf1cab4326","repo":"RocketChat/Rocket.Chat","slug":"error-duplicate-role-names-not-allowed-89989e","errorCode":"error-duplicate-role-names-not-allowed","errorMessage":"Role name already exists","messagePattern":"Role name already exists","errorType":"exception","errorClass":"MeteorError","httpStatus":null,"severity":"error","filePath":"apps/meteor/ee/server/lib/roles/updateRole.ts","lineNumber":30,"sourceCode":"export const updateRole = async (\n\troleId: IRole['_id'],\n\troleData: Omit<IRole, '_id' | '_updatedAt'>,\n\toptions: UpdateRoleOptions = {},\n): Promise<IRole> => {\n\tconst role = await Roles.findOneById(roleId);\n\n\tif (!role) {\n\t\tthrow new MeteorError('error-invalid-roleId', 'This role does not exist');\n\t}\n\n\tif (role.protected && ((roleData.name && roleData.name !== role.name) || (roleData.scope && roleData.scope !== role.scope))) {\n\t\tthrow new MeteorError('error-role-protected', 'Role is protected');\n\t}\n\n\tif (roleData.name) {\n\t\tconst otherRole = await Roles.findOneByName(roleData.name, { projection: { _id: 1 } });\n\t\tif (otherRole && otherRole._id !== role._id) {\n\t\t\tthrow new MeteorError('error-duplicate-role-names-not-allowed', 'Role name already exists');\n\t\t}\n\t} else {\n\t\troleData.name = role.name;\n\t}\n\n\tif (roleData.scope) {\n\t\tif (!isValidRoleScope(roleData.scope)) {\n\t\t\tthrow new MeteorError('error-invalid-scope', 'Invalid scope');\n\t\t}\n\t} else {\n\t\troleData.scope = role.scope;\n\t}\n\n\tawait Roles.updateById(roleId, roleData.name, roleData.scope, roleData.description, roleData.mandatory2fa);\n\n\tvoid notifyOnRoleChangedById(roleId);\n\n\tif (options.broadcastUpdate) {","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/ee/server/lib/roles/updateRole.ts#L12-L48","documentation":"When a role update includes a new name, updateRole checks whether a different role already owns it: Roles.findOneByName(roleData.name) returning a document whose _id differs from the target throws MeteorError('error-duplicate-role-names-not-allowed', 'Role name already exists'). Renaming a role to its own current name is fine (same _id); colliding with any other role is not.","triggerScenarios":"Renaming role A to a name already held by role B - including built-ins like 'moderator'; or two admins concurrently renaming different roles onto the same currently-free name, where the second update hits the duplicate.","commonSituations":"Consolidation scripts renaming roles to a canonical name that already exists; stale UI role lists that do not show a just-created role; import tooling that renames to standard names without checking.","solutions":["Pick a unique name, or refresh the roles list to see current names before renaming.","If the goal is to merge two roles, move user assignments to the existing role and delete the redundant one instead of renaming onto it.","Check Roles.findOneByName(newName) with an _id comparison before submitting the rename."],"exampleFix":"// before\nawait updateRole(roleA._id, { name: 'support' }); // 'support' already owned by role B -> throws\n\n// after\nconst other = await Roles.findOneByName('support');\nif (other && other._id !== roleA._id) {\n\tawait updateRole(roleA._id, { name: 'support-tier2' }); // unique name\n} else {\n\tawait updateRole(roleA._id, { name: 'support' });\n}","handlingStrategy":"validation","validationCode":"if (roleData.name) {\n\tconst other = await Roles.findOneByName(roleData.name, { projection: { _id: 1 } });\n\tif (other && other._id !== roleId) {\n\t\t// name owned by another role; pick a different name before calling updateRole\n\t}\n}","typeGuard":null,"tryCatchPattern":"try {\n\tawait updateRole(roleId, roleData);\n} catch (e: any) {\n\tif (e?.error === 'error-duplicate-role-names-not-allowed') { surface(`Role name '${roleData.name}' is taken`); return; }\n\tthrow e;\n}","preventionTips":["Refresh the roles list before renaming to see current names.","For consolidation, migrate assignments to the existing role instead of renaming onto it.","Treat role names as globally unique identifiers in validation schemas."],"tags":["roles","permissions","duplicate-entry","enterprise"],"backgroundTag":"duplicate-entry","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}