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
Thrown by POST roles.create when Roles.findOneByIdOrName(name) returns an existing role. Role names (and IDs) must be unique; passing a name that collides with an existing role (by _id or by name) is rejected. Returns Meteor.Error error-duplicate-role-names-not-allowed.
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 f9d3ec372b)
Solutions
- Check for an existing role via GET /v1/roles.list before creating, and reuse it if present.
- Choose a unique role name.
- If the duplicate was accidental, delete the existing role first (DELETE /v1/roles.delete) if it is not protected.
- Make setup scripts idempotent: skip creation when the role already exists.
Example fix
// before: unconditional create
await POST('/v1/roles.create', { name: 'support-agent' });
// after: idempotent create
const existing = (await GET('/v1/roles.list')).roles.find(r => r.name === 'support-agent');
if (!existing) {
await POST('/v1/roles.create', { name: 'support-agent' });
} Defensive patterns
Strategy: validation
Validate before calling
// Idempotency check before create
const { roles } = await api.get('roles.list');
if (roles.some(r => r.name === body.name || r._id === body.name)) {
throw new Error(`Role '${body.name}' already exists`);
}
await api.post('roles.create', body); Try / catch
try {
await api.post('roles.create', body);
} catch (e) {
if (isMeteorError(e, 'error-duplicate-role-names-not-allowed')) {
// role already exists; fetch and reuse it
return api.get('roles.list').then(r => r.roles.find(x => x.name === body.name));
}
throw e;
} Prevention
- Make role-provisioning scripts idempotent (lookup-then-create).
- Avoid reusing built-in role names (admin, user, livechat-agent, etc.).
When it happens
Trigger: Calling POST /v1/roles.create with a body whose name matches an existing role's name or _id. Happens on retry after a partial create, on duplicate submissions, or when re-importing a role set.
Common situations: Re-running a setup script that creates roles; name collision with a built-in role (admin, user, livechat-agent); case sensitivity surprises (lookup is by DB exact match).
Related errors
- error-invalid-param
- error-param-not-provided
- error-invalid-roleId
- error-invalid-user
- The "${name}" parameter must be a valid date.
AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12).
Data as JSON: /api/errors/0db5590703d0c5ac.
Report an issue: GitHub.