{"record":{"id":"a14be4c13dca2180","repo":"RocketChat/Rocket.Chat","slug":"error-duplicate-role-names-not-allowed-a14be4","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/insertRole.ts","lineNumber":16,"sourceCode":"import { api, MeteorError } from '@rocket.chat/core-services';\nimport type { IRole } from '@rocket.chat/core-typings';\nimport { Roles } from '@rocket.chat/models';\n\nimport { isValidRoleScope } from '../../../../lib/roles/isValidRoleScope';\nimport { notifyOnRoleChanged } from '../../../../server/lib/notifyListener';\n\ntype InsertRoleOptions = {\n\tbroadcastUpdate?: boolean;\n};\n\nexport const insertRoleAsync = async (roleData: Omit<IRole, '_id' | '_updatedAt'>, options: InsertRoleOptions = {}): Promise<IRole> => {\n\tconst { name, scope, description, mandatory2fa } = roleData;\n\n\tif (await Roles.findOneByName(name)) {\n\t\tthrow new MeteorError('error-duplicate-role-names-not-allowed', 'Role name already exists');\n\t}\n\n\tif (!isValidRoleScope(scope)) {\n\t\tthrow new MeteorError('error-invalid-scope', 'Invalid scope');\n\t}\n\n\tconst role = await Roles.createWithRandomId(name, scope, description, false, mandatory2fa);\n\n\tvoid notifyOnRoleChanged(role);\n\n\tif (options.broadcastUpdate) {\n\t\tvoid api.broadcast('user.roleUpdate', {\n\t\t\ttype: 'changed',\n\t\t\t_id: role._id,\n\t\t});\n\t}\n\n\treturn role;","sourceCodeStart":1,"sourceCodeEnd":34,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/ee/server/lib/roles/insertRole.ts#L1-L34","documentation":"insertRoleAsync (the Enterprise role-creation helper) refuses to create a role whose name already exists: if Roles.findOneByName(name) finds a document it throws MeteorError('error-duplicate-role-names-not-allowed', 'Role name already exists'). Role names are the globally unique key used for permission assignment, so duplicates are rejected at the application layer before any write.","triggerScenarios":"Calling insertRoleAsync (backing role creation for the Permissions admin surface) with a name that already exists - including built-ins such as admin, moderator, livechat-agent, guest, bot, and any previously created custom role.","commonSituations":"Re-running a seeding/provisioning script that creates the same custom role twice; creating a role whose name collides with a built-in; two concurrent requests racing to create the same role where the loser hits the duplicate check.","solutions":["Make provisioning idempotent: look the role up by name first and reuse the existing one instead of erroring.","Choose a name that does not collide with built-in or existing roles.","On a race, catch the error and re-fetch - the role now exists and can be used."],"exampleFix":"// before\nawait insertRoleAsync({ name: 'auditor', scope: 'Users', description: 'Read-only audits' }); // throws if 'auditor' exists\n\n// after\nconst existing = await Roles.findOneByName('auditor');\nif (existing) return existing;\nreturn insertRoleAsync({ name: 'auditor', scope: 'Users', description: 'Read-only audits' });","handlingStrategy":"validation","validationCode":"const existing = await Roles.findOneByName(name);\nif (existing) {\n\t// reuse the existing role instead of creating a duplicate\n}","typeGuard":null,"tryCatchPattern":"try {\n\tawait insertRoleAsync(roleData);\n} catch (e: any) {\n\tif (e?.error === 'error-duplicate-role-names-not-allowed') { return Roles.findOneByName(roleData.name); } // created concurrently; reuse\n\tthrow e;\n}","preventionTips":["Make role-provisioning scripts idempotent (find-or-create).","Avoid names matching built-in roles (admin, moderator, guest, bot, livechat-agent).","Reflect uniqueness in the role form: check the name against the roles list before submit."],"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"}