{"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/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/ee/server/lib/syncUserRoles.ts#L58-L91","documentation":"Before applying role changes, syncUserRoles detects a pure-guest user (existingRoles.length === 1 && existingRoles[0] === 'guest') and calls License.shouldPreventAction('activeUsers'); if the Enterprise license's active-user seats are exhausted it throws Error('error-license-user-limit-reached'). Adding any role to a guest converts them into an active user - the licensed quantity - so the operation is blocked before any write. Non-guest users never hit this branch.","triggerScenarios":"Promoting a guest-only user to 'user', agent, or any additional role via syncUserRoles on a workspace already at its licensed active-user limit. The check is wasGuest-gated, so role changes for already-active users proceed normally.","commonSituations":"Bulk-invite or SSO auto-role assignment pushing active users past the seat count; guest-heavy community workspaces promoting users after a license downgrade; expired trials reverting to fewer seats than the current active-user count.","solutions":["Free active seats first: deactivate or remove inactive users, then retry the role change.","Increase the license's active-user allowance.","Queue guest promotions and process them as seats free up; pre-check License.shouldPreventAction('activeUsers') before attempting bulk changes."],"exampleFix":"// before\nawait syncUserRoles(guestUid, ['user'], opts); // throws error-license-user-limit-reached at the seat ceiling\n\n// after\nif (await License.shouldPreventAction('activeUsers')) {\n\tawait setUserActiveStatus(inactiveUid, false); // free a seat first\n}\nawait syncUserRoles(guestUid, ['user'], opts);","handlingStrategy":"validation","validationCode":"const user = await Users.findOneById(uid, { projection: { roles: 1 } });\nconst isGuestOnly = (user?.roles?.length ?? 0) === 1 && user?.roles?.[0] === 'guest';\nif (isGuestOnly && (await License.shouldPreventAction('activeUsers'))) {\n\t// promoting this guest would exceed active-user seats; block before calling syncUserRoles\n}","typeGuard":null,"tryCatchPattern":"try {\n\tawait syncUserRoles(uid, newRoleList, opts);\n} catch (e: any) {\n\tif (e?.message === 'error-license-user-limit-reached') { surface('Free a seat (deactivate a user) or raise the license limit'); return; }\n\tthrow e;\n}","preventionTips":["Track active-user count against the license before bulk role promotions.","Monitor seat utilization and clean up inactive users proactively.","Remember the gate only applies to guest-only users gaining their first additional role."],"tags":["roles","licensing","user-limits","enterprise"],"backgroundTag":"license-limit-reached","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}