{"record":{"id":"8110df13fb64cf6a","repo":"n8n-io/n8n","slug":"maximum-number-of-users-reached","errorCode":null,"errorMessage":"Maximum number of users reached","messagePattern":"Maximum number of users reached","errorType":"exception","errorClass":"ForbiddenError","httpStatus":403,"severity":"error","filePath":"packages/cli/src/auth/auth.service.ts","lineNumber":242,"sourceCode":"\t\t\t}\n\t\t} catch (e) {\n\t\t\tthis.logger.warn('failed to invalidate auth token', { error: (e as Error).message });\n\t\t}\n\t}\n\n\tissueCookie(\n\t\tres: Response,\n\t\tuser: User,\n\t\tusedMfa: boolean,\n\t\tbrowserId?: string,\n\t\tisEmbed?: boolean,\n\t\tcookieOverrides?: { sameSite?: 'strict' | 'lax' | 'none'; secure?: boolean },\n\t) {\n\t\t// TODO: move this check to the login endpoint in AuthController\n\t\t// If the instance has exceeded its user quota, prevent non-owners from logging in\n\t\tconst isWithinUsersLimit = this.license.isWithinUsersLimit();\n\t\tif (user.role.slug !== GLOBAL_OWNER_ROLE.slug && !isWithinUsersLimit) {\n\t\t\tthrow new ForbiddenError(RESPONSE_ERROR_MESSAGES.USERS_QUOTA_REACHED);\n\t\t}\n\n\t\tconst token = this.issueJWT(user, usedMfa, browserId, isEmbed);\n\t\tconst { samesite, secure } = this.globalConfig.auth.cookie;\n\t\tres.cookie(AUTH_COOKIE_NAME, token, {\n\t\t\tmaxAge: this.jwtExpiration * Time.seconds.toMilliseconds,\n\t\t\thttpOnly: true,\n\t\t\tsameSite: cookieOverrides?.sameSite ?? samesite,\n\t\t\tsecure: cookieOverrides?.secure ?? secure,\n\t\t});\n\t}\n\n\tissueJWT(user: User, usedMfa: boolean = false, browserId?: string, isEmbed?: boolean) {\n\t\tconst payload: AuthJwtPayload = {\n\t\t\tid: user.id,\n\t\t\thash: this.createJWTHash(user),\n\t\t\tbrowserId: browserId && this.hash(browserId),\n\t\t\tusedMfa,","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/cli/src/auth/auth.service.ts#L224-L260","documentation":"Thrown by AuthService.issueCookie as a ForbiddenError (RESPONSE_ERROR_MESSAGES.USERS_QUOTA_REACHED) when a non-owner user attempts to log in and the instance has reached its licensed user quota. Owners can still log in to manage the license/seats.","triggerScenarios":"A non-owner user submits valid credentials at login (or SSO completes) while the active license's seat count is at or above its limit. The check fires inside issueCookie before a JWT is issued, so no session is created.","commonSituations":"Community edition limits reached; an expired/downgraded license that shrunk the seat count; offboarded users were not deactivated so seats remain consumed; bulk-provisioned users beyond the licensed tier.","solutions":["An owner logs in and upgrades the license or increases the seat count.","Deactivate or delete inactive users to free seats (UserManagement), then have the user retry login.","Verify the license is correctly applied and not expired (n8n logs the license quota at startup)."],"exampleFix":"n/a (operational resolution, not a code fix)","handlingStrategy":"try-catch","validationCode":"n/a (server-side quota check; client cannot pre-validate licensed seat count)","typeGuard":"function isQuotaError(error: unknown): boolean {\n  return error instanceof Error && error.message.includes('Maximum number of users reached');\n}","tryCatchPattern":"// Login flow\ntry {\n  await login(email, password);\n} catch (e) {\n  if (isQuotaError(e)) { /* surface 'contact admin to free a seat' to the user */ }\n  else throw e;\n}","preventionTips":["Monitor licensed seat usage vs. user count as an admin.","Deactivate offboarded users promptly to free seats.","Keep the license tier aligned with headcount; renew before expiry."],"tags":["auth","license","users","quota","http-403"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}