{"record":{"id":"6657c7446c97957c","repo":"RocketChat/Rocket.Chat","slug":"error-action-not-allowed-6657c7","errorCode":"error-action-not-allowed","errorMessage":"This is an enterprise feature","messagePattern":"This is an enterprise feature","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"warning","filePath":"apps/meteor/ee/server/api/roles.ts","lineNumber":115,"sourceCode":"\trequired: ['role', 'success'],\n\tadditionalProperties: false,\n});\n\nAPI.v1.post(\n\t'roles.create',\n\t{\n\t\tauthRequired: true,\n\t\tlicense: ['custom-roles'],\n\t\tbody: isRoleCreateProps,\n\t\tresponse: {\n\t\t\t200: roleResponseSchema,\n\t\t\t401: validateUnauthorizedErrorResponse,\n\t\t\t400: validateBadRequestErrorResponse,\n\t\t},\n\t},\n\tasync function action() {\n\t\tif (!License.hasModule('custom-roles')) {\n\t\t\tthrow new Meteor.Error('error-action-not-allowed', 'This is an enterprise feature');\n\t\t}\n\n\t\tconst { userId } = this;\n\n\t\tif (!userId || !(await hasPermissionAsync(userId, 'access-permissions'))) {\n\t\t\tthrow new Meteor.Error('error-action-not-allowed', 'Accessing permissions is not allowed');\n\t\t}\n\n\t\tconst { name, scope, description, mandatory2fa } = this.bodyParams;\n\n\t\tif (await Roles.findOneByIdOrName(name)) {\n\t\t\tthrow new Meteor.Error('error-duplicate-role-names-not-allowed', 'Role name already exists');\n\t\t}\n\n\t\tconst roleData = {\n\t\t\tdescription: description || '',\n\t\t\t...(mandatory2fa !== undefined && { mandatory2fa }),\n\t\t\tname,","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/f9d3ec372bb580fa8d036f94cf03925a478ef768/apps/meteor/ee/server/api/roles.ts#L97-L133","documentation":"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.","triggerScenarios":"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.","commonSituations":"CE deployment; expired Enterprise license; EE license missing the custom-roles add-on; refactored route that dropped the license: [] gate.","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."],"exampleFix":"// before: route handler reached without a license gate\nAPI.v1.post('roles.create', { authRequired: true }, action);\n\n// after: declare the license requirement at route level so middleware rejects cleanly\nAPI.v1.post('roles.create', { authRequired: true, license: ['custom-roles'], body: isRoleCreateProps }, action);","handlingStrategy":"validation","validationCode":"// Gate custom-role creation on the license module\nasync function canManageCustomRoles(): Promise<boolean> {\n  const { license } = await (await fetch('/api/v1/license.get', { headers: authHeaders() })).json();\n  return Array.isArray(license?.modules) && license.modules.includes('custom-roles');\n}\nif (!(await canManageCustomRoles())) {\n  throw new Error('custom-roles module not licensed');\n}","typeGuard":"function hasCustomRolesLicense(modules: unknown): modules is string[] {\n  return Array.isArray(modules) && modules.includes('custom-roles');\n}","tryCatchPattern":"try {\n  await api.post('roles.create', body);\n} catch (e) {\n  if (isMeteorError(e, 'error-action-not-allowed') && /enterprise/i.test(e.reason)) {\n    showUpgradePrompt('custom-roles');\n    return;\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["enterprise","license","roles","rest-api"],"backgroundTag":null,"analyzedSha":"f9d3ec372bb580fa8d036f94cf03925a478ef768","analyzedAt":"2026-08-12T19:07:17.372Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}