RocketChat/Rocket.Chat · error · Meteor.Error
error-duplicate-role-names-not-allowed
error-duplicate-role-names-not-allowed
Error message
Role name already exists
What it means
roles.create rejects names that collide with any existing role: Roles.findOneByIdOrName(name) matches by _id OR by name, so re-using 'admin', 'moderator', any other built-in name, or an already-created custom role name throws 'error-duplicate-role-names-not-allowed' ('Role name already exists').
Solutions
- Choose a unique name, e.g. prefixed with the integration ('zendesk-auditor')
- Fetch GET /v1/roles.list and check the name does not exist before creating
- If the role already exists and you want to change it, call POST /v1/roles.update instead
Example fix
// before
await POST('/api/v1/roles.create', { name: 'moderator' }); // built-in role → duplicate
// after
const { roles } = await GET('/api/v1/roles.list');
if (!roles.some((role) => role.name === 'moderator')) {
await POST('/api/v1/roles.create', { name: 'moderator' });
} Defensive patterns
Strategy: validation
Validate before calling
import { Roles } from '@rocket.chat/models';
if (await Roles.findOneByIdOrName(newRoleName)) {
throw new Error(`role name '${newRoleName}' already taken`);
}
await POST('/api/v1/roles.create', { name: newRoleName }); Try / catch
try {
await POST('/api/v1/roles.create', payload);
} catch (error) {
if (error.error === 'error-duplicate-role-names-not-allowed') {
return GET('/api/v1/roles.list'); // idempotent path: reuse the existing role
}
throw error;
} Prevention
- Namespace custom role names (e.g. 'billing-viewer') to avoid built-in collisions
- Make provisioning scripts idempotent: check-then-create instead of blind create
- Remember the check matches _id AND name
When it happens
Trigger: POST /v1/roles.create with a name equal to an existing role's name or _id, e.g. 'moderator' or the literal id string of another role.
Common situations: Re-running a non-idempotent provisioning script; picking obvious names that collide with built-in roles; name compared exactly (case-sensitive) so 'Moderator' vs 'moderator' confusion.
Related errors
- duplicated-account
- error-action-not-allowed
- error-business-hour-name-already-in-use
- error-contact-manager-not-found
- error-duplicated-sla
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/0db5590703d0c5ac.
Report an issue: GitHub.
Appendix: source
Thrown at apps/meteor/ee/server/api/roles.ts:127
401: validateUnauthorizedErrorResponse,
400: validateBadRequestErrorResponse,
},
},
async function action() {
if (!License.hasModule('custom-roles')) {
throw new Meteor.Error('error-action-not-allowed', 'This is an enterprise feature');
}
const { userId } = this;
if (!userId || !(await hasPermissionAsync(userId, 'access-permissions'))) {
throw new Meteor.Error('error-action-not-allowed', 'Accessing permissions is not allowed');
}
const { name, scope, description, mandatory2fa } = this.bodyParams;
if (await Roles.findOneByIdOrName(name)) {
throw new Meteor.Error('error-duplicate-role-names-not-allowed', 'Role name already exists');
}
const roleData = {
description: description || '',
...(mandatory2fa !== undefined && { mandatory2fa }),
name,
scope: scope || 'Users',
protected: false,
};
const options = {
broadcastUpdate: settings.get<boolean>('UI_DisplayRoles'),
};
const role = await insertRoleAsync(roleData, options);
return API.v1.success({ role });
},View on GitHub (pinned to b2c16d5842)