RocketChat/Rocket.Chat · warning · Meteor.Error
error-action-not-allowed
error-action-not-allowed
Error message
This is an enterprise feature
What it means
Thrown by POST roles.create when the server license does not include the 'custom-roles' module. Custom (user-defined) roles are an Enterprise feature; the route also declares license: ['custom-roles'] at the route level, so this in-action check is a redundant safety net in case the license middleware is bypassed or the route-level gate is later removed. Returns Meteor.Error error-action-not-allowed.
Source
Thrown at apps/meteor/ee/server/api/roles.ts:115
required: ['role', 'success'],
additionalProperties: false,
});
API.v1.post(
'roles.create',
{
authRequired: true,
license: ['custom-roles'],
body: isRoleCreateProps,
response: {
200: roleResponseSchema,
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,View on GitHub (pinned to f9d3ec372b)
Solutions
- Apply an Enterprise license that includes the custom-roles module.
- If you cannot get custom-roles, use only the built-in roles instead of creating new ones.
- Restore the route-level license: ['custom-roles'] gate if it was removed, so the middleware rejects pre-handler.
- Confirm the license module list via the license/workspace API.
Example fix
// before: route handler reached without a license gate
API.v1.post('roles.create', { authRequired: true }, action);
// after: declare the license requirement at route level so middleware rejects cleanly
API.v1.post('roles.create', { authRequired: true, license: ['custom-roles'], body: isRoleCreateProps }, action); Defensive patterns
Strategy: validation
Validate before calling
// Gate custom-role creation on the license module
async function canManageCustomRoles(): Promise<boolean> {
const { license } = await (await fetch('/api/v1/license.get', { headers: authHeaders() })).json();
return Array.isArray(license?.modules) && license.modules.includes('custom-roles');
}
if (!(await canManageCustomRoles())) {
throw new Error('custom-roles module not licensed');
} Type guard
function hasCustomRolesLicense(modules: unknown): modules is string[] {
return Array.isArray(modules) && modules.includes('custom-roles');
} Try / catch
try {
await api.post('roles.create', body);
} catch (e) {
if (isMeteorError(e, 'error-action-not-allowed') && /enterprise/i.test(e.reason)) {
showUpgradePrompt('custom-roles');
return;
}
throw e;
} Prevention
- Keep the route-level license: ['custom-roles'] gate in place so the middleware rejects before the handler.
- Hide the Create Role button when the module is absent.
When it happens
Trigger: Calling POST /v1/roles.create on Community edition or on an Enterprise license without custom-roles. Normally the route-level license middleware rejects earlier, so seeing this exact throw means the middleware gate was removed or the action was invoked directly.
Common situations: CE deployment; expired Enterprise license; EE license missing the custom-roles add-on; refactored route that dropped the license: [] gate.
Related errors
- error-action-not-allowed
- error-max-guests-number-reached
- error-license-user-limit-reached
- error-duplicate-role-names-not-allowed
- error-action-not-allowed
AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12).
Data as JSON: /api/errors/6657c7446c97957c.
Report an issue: GitHub.