n8n-io/n8n · error · ForbiddenError

Maximum number of users reached

Error message

Maximum number of users reached

What it means

ForbiddenError (HTTP 403) thrown from auth.controller.ts:235 when resolving a signup invitation token while the instance license reports the active user count is already at the entitlement ceiling. `RESPONSE_ERROR_MESSAGES.USERS_QUOTA_REACHED` is the literal 'Maximum number of users reached'. The license check (`this.license.isWithinUsersLimit()`) runs on every invitation-link resolve, so even an already-issued invite becomes unresolvable once a later signup filled the last seat.

Source

Thrown at packages/cli/src/controllers/auth.controller.ts:235

		}

		if (!payload.token) {
			this.logger.debug('Request to resolve signup token failed because token is missing');
			throw new BadRequestError('Token is required');
		}

		const { inviterId, inviteeId } = await this.userService.getInvitationIdsFromPayload(
			payload.token,
		);

		const isWithinUsersLimit = this.license.isWithinUsersLimit();

		if (!isWithinUsersLimit) {
			this.logger.debug('Request to resolve signup token failed because of users quota reached', {
				inviterId,
				inviteeId,
			});
			throw new ForbiddenError(RESPONSE_ERROR_MESSAGES.USERS_QUOTA_REACHED);
		}

		const users = await this.userRepository.findManyByIds([inviterId, inviteeId], {
			includeRole: true,
		});

		if (users.length !== 2) {
			this.logger.debug(
				'Request to resolve signup token failed because the ID of the inviter and/or the ID of the invitee were not found in database',
				{ inviterId, inviteeId },
			);
			throw new BadRequestError('Invalid invite URL');
		}

		const invitee = users.find((user) => user.id === inviteeId);
		if (!invitee || invitee.password) {
			this.logger.error('Invalid invite URL - invitee already setup', {
				inviterId,

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Raise the seat entitlement: upgrade the license / add seats in the billing console, then reactivate via `n8n license:info` and re-resolve the same invite URL.
  2. Free a seat: deactivate or delete an unused user in Settings > Users, confirm the count drops, then re-open the invite link.
  3. Re-issue the invitation after the seat change so the resolve runs against an in-limit state.
  4. If unexpected, audit actual user count (`SELECT COUNT(*) FROM user`) against the license entitlement — orphaned/inactive users still consume seats.

Example fix

// before — invite resolves against a saturated instance
GET /resolve-signup-token?token=...
// 403 Maximum number of users reached

// after — free a seat, then re-resolve
DELETE /users/<inactive-user-id>   // admin frees the seat
GET /resolve-signup-token?token=... // succeeds
Defensive patterns

Strategy: validation

Validate before calling

// Before mailing/reusing an invite, check license headroom
import { Service } from '@n8n/di';

// in an admin client
async function canInvite(license: LicenseService, userService: UserService) {
  const activeUsers = await userService.count(); // your repo method
  return license.isWithinUsersLimit() && activeUsers < license.userLimit;
}

// only re-share the invite URL when canInvite(...) returns true

Try / catch

// fetch invite resolve
try {
  await fetch('/resolve-signup-token?token=' + tok);
} catch (e) {
  if (e.status === 403 && /quota|maximum/i.test(e.message)) {
    // surface 'request more seats' to the admin instead of retrying
  }
}

Prevention

When it happens

Trigger: A GET/POST to the signup-invite resolve endpoint with a valid token whose `inviterId`/`inviteeId` decode successfully, but the license's current seat usage equals the entitlement. Reproducible by inviting user N+1 after the Nth user already claimed a seat, or by an admin deleting and re-issuing an invite after headcount grew.

Common situations: Self-hosted instances on a fixed-seat Enterprise/Pro plan that onboarded up to the limit; trial licenses expiring mid-onboarding; stale invite links mailed before a hiring freeze hit. Cloud-managed instances rarely hit this because seat enforcement is upstream.

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/6e55a1ad813e591d. Report an issue: GitHub.