{"record":{"id":"68715a8e2033f161","repo":"RocketChat/Rocket.Chat","slug":"error-license-user-limit-reached-68715a","errorCode":null,"errorMessage":"error-license-user-limit-reached","messagePattern":"error-license-user-limit-reached","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/ee/server/lib/syncUserRoles.ts","lineNumber":76,"sourceCode":"\tnewRoleList: Array<IRole['_id']>,\n\t{ allowedRoles, skipRemovingRoles, scope }: setUserRolesOptions,\n): Promise<void> {\n\tconst user = await Users.findOneById<Pick<IUser, '_id' | 'username' | 'roles'>>(uid, { projection: { username: 1, roles: 1 } });\n\tif (!user) {\n\t\tthrow new Error('error-user-not-found');\n\t}\n\n\tconst existingRoles = user.roles;\n\tconst rolesToAdd = filterRoleList(newRoleList, existingRoles, allowedRoles);\n\tconst rolesToRemove = filterRoleList(existingRoles, newRoleList, allowedRoles);\n\n\tif (!rolesToAdd.length && !rolesToRemove.length) {\n\t\treturn;\n\t}\n\n\tconst wasGuest = existingRoles.length === 1 && existingRoles[0] === 'guest';\n\tif (wasGuest && (await License.shouldPreventAction('activeUsers'))) {\n\t\tthrow new Error('error-license-user-limit-reached');\n\t}\n\n\tif (rolesToAdd.length && (await addUserRolesAsync(uid, rolesToAdd, scope))) {\n\t\tbroadcastRoleChange('added', rolesToAdd, user);\n\t}\n\n\tif (skipRemovingRoles || !rolesToRemove.length) {\n\t\treturn;\n\t}\n\n\tif (await removeUserFromRolesAsync(uid, rolesToRemove, scope)) {\n\t\tbroadcastRoleChange('removed', rolesToRemove, user);\n\t}\n}\n","sourceCodeStart":58,"sourceCodeEnd":91,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/f9d3ec372bb580fa8d036f94cf03925a478ef768/apps/meteor/ee/server/lib/syncUserRoles.ts#L58-L91","documentation":"Thrown by syncUserRoles in syncUserRoles.ts:76 when promoting a former guest (existingRoles === ['guest']) to a non-guest role while License.shouldPreventAction('activeUsers') is true — i.e. the EE license's active-user seat limit would be exceeded. NOTE: plain `new Error('error-license-user-limit-reached')`.","triggerScenarios":"Assigning any non-guest role to a user whose only current role is 'guest', on a licensed instance already at its active-user cap. The check only fires on the guest→active transition (wasGuest).","commonSituations":"Trial/SE license at seat capacity; bulk role assignment that promotes many guests; admin assigns 'agent'/'admin' to a guest without checking seats.","solutions":["Free an active-user seat (deactivate or demote another user to guest) before promoting.","Upgrade the license tier to raise the activeUsers cap.","Pre-check License.shouldPreventAction('activeUsers') and the guest state before calling syncUserRoles."],"exampleFix":"// before\nawait syncUserRoles(uid, ['agent'], opts);\n\n// after\nconst u = await Users.findOneById(uid, { projection: { roles: 1 } });\nconst wasGuest = u?.roles?.length === 1 && u.roles[0] === 'guest';\nif (wasGuest && (await License.shouldPreventAction('activeUsers'))) {\n  throw new Error('no free seats; upgrade license or free a seat');\n}\nawait syncUserRoles(uid, ['agent'], opts);","handlingStrategy":"validation","validationCode":"const u = await Users.findOneById(uid, { projection: { roles: 1 } });\nconst wasGuest = u?.roles?.length === 1 && u.roles[0] === 'guest';\nif (wasGuest && (await License.shouldPreventAction('activeUsers'))) {\n  throw new Error('no free seats');\n}","typeGuard":"const isGuestOnly = (roles: string[] | undefined) =>\n  Array.isArray(roles) && roles.length === 1 && roles[0] === 'guest';","tryCatchPattern":"try { await syncUserRoles(uid, newRoles, opts); }\ncatch (e) {\n  if (e instanceof Error && e.message === 'error-license-user-limit-reached') { /* free a seat */ return; }\n  throw e;\n}","preventionTips":["Check active-user seats before promoting guests in bulk.","Surface current seat usage in the role-assignment UI."],"tags":["roles","license","seats","enterprise","plain-error"],"backgroundTag":null,"analyzedSha":"f9d3ec372bb580fa8d036f94cf03925a478ef768","analyzedAt":"2026-08-12T19:07:17.372Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}