RocketChat/Rocket.Chat · error · MeteorError

error-max-guests-number-reached

error-max-guests-number-reached

Error message

Maximum number of guests reached.

What it means

Thrown by validateUserRoles (wired into user create/update and role assignment flows as method 'insertOrUpdateUser') when a user is becoming a guest (roles exactly ['guest'] and previously not) and the license's guest-user allowance is exhausted (License.shouldPreventAction('guestUsers')). App and bot user types bypass the check. It enforces the enterprise guest-seat limit at the moment the guest role is granted.

Solutions

  1. Convert other guests back to regular users (or deactivate them) to free guest seats, then retry
  2. Apply a license with a higher guestUsers allowance (Administration > License)
  3. For imports, throttle guest role assignment until the license is upgraded

Example fix

// before: assigning the guest role and letting the hook throw
await insertOrUpdateUser({ ...userData, roles: ['guest'] });

// after: pre-check the same license gate
import { License } from '@rocket.chat/license';
const becomingGuest = (userData.roles ?? []).includes('guest') && (currentUserData?.roles ?? []).join() !== 'guest';
if (becomingGuest && (await License.shouldPreventAction('guestUsers'))) {
  throw new Error('Guest seat limit reached; upgrade the license first');
}
await insertOrUpdateUser({ ...userData, roles: ['guest'] });
Defensive patterns

Strategy: validation

Validate before calling

import { License } from '@rocket.chat/license';

const canAddGuest = async (): Promise<boolean> => !(await License.shouldPreventAction('guestUsers'));

Type guard

const isGuestOnlyRoles = (roles?: string[]): boolean =>
  Boolean(roles?.includes('guest') && roles.length === 1);

Try / catch

try {
  await insertOrUpdateUser({ ...userData, roles: ['guest'] });
} catch (err: any) {
  if (err?.error === 'error-max-guests-number-reached') {
    // free a guest seat or upgrade the license before retrying the role assignment
  }
  throw err;
}

Prevention

When it happens

Trigger: Creating a user with only the guest role, or editing an existing user's roles down to just 'guest', past the licensed guestUsers limit; SSO/LDAP provisioning assigning the guest role to new external users.

Common situations: External-collaboration onboarding exceeding purchased guest seats; guest role bulk-assigned during imports; license tier with few guest seats.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18). Data as JSON: /api/errors/e5ba5a998362a50c. Report an issue: GitHub.

Appendix: source

Thrown at apps/meteor/ee/server/lib/authorization/validateUserRoles.ts:26

	const isApp = Boolean(userData.type === 'app');
	const wasApp = Boolean(currentUserData?.type === 'app');

	const isBot = Boolean(userData.type === 'bot');
	const wasBot = Boolean(currentUserData?.type === 'bot');

	const isGuest = Boolean(userData.roles?.includes('guest') && userData.roles.length === 1);
	const wasGuest = Boolean(currentUserData?.roles?.includes('guest') && currentUserData.roles.length === 1);

	const isSpecialType = isApp || isBot;

	const hasGuestToChanged = isGuest && !wasGuest;

	if (isSpecialType) {
		return;
	}

	if (hasGuestToChanged && (await License.shouldPreventAction('guestUsers'))) {
		throw new MeteorError('error-max-guests-number-reached', 'Maximum number of guests reached.', {
			method: 'insertOrUpdateUser',
			field: 'Assign_role',
		});
	}

	if (isGuest) {
		return;
	}

	const isActive = Boolean(userData.active !== false);
	const wasActive = currentUserData && currentUserData?.active !== false;

	const hasRemovedSpecialType = (wasApp && !isApp) || (wasBot && !isBot);

	if (!isActive) {
		return;
	}

View on GitHub (pinned to b2c16d5842)