{"record":{"id":"3aa023438561074a","repo":"n8n-io/n8n","slug":"maximum-number-of-users-reached-3aa023","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/controllers/password-reset.controller.ts","lineNumber":98,"sourceCode":"\t\t\t\t'Email sending must be set up in order to request a password reset email',\n\t\t\t);\n\t\t}\n\n\t\ttry {\n\t\t\tconst { email } = payload;\n\n\t\t\t// User should just be able to reset password if one is already present\n\t\t\tconst user = await this.userRepository.findNonShellUser(email);\n\t\t\tif (!user) {\n\t\t\t\tthis.logger.debug('No user found in the system');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (user.role.slug !== GLOBAL_OWNER_ROLE.slug && !this.license.isWithinUsersLimit()) {\n\t\t\t\tthis.logger.debug(\n\t\t\t\t\t'Request to send password reset email failed because the user limit was reached',\n\t\t\t\t);\n\t\t\t\tthrow new ForbiddenError(RESPONSE_ERROR_MESSAGES.USERS_QUOTA_REACHED);\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\t(isSamlCurrentAuthenticationMethod() || isOidcCurrentAuthenticationMethod()) &&\n\t\t\t\t!(hasGlobalScope(user, 'user:resetPassword') || user.settings?.allowSSOManualLogin === true)\n\t\t\t) {\n\t\t\t\tconst currentAuthenticationMethod = isSamlCurrentAuthenticationMethod() ? 'SAML' : 'OIDC';\n\t\t\t\tthis.logger.debug(\n\t\t\t\t\t`Request to send password reset email failed because login is handled by ${currentAuthenticationMethod}`,\n\t\t\t\t);\n\t\t\t\tthrow new ForbiddenError(\n\t\t\t\t\t`Login is handled by ${currentAuthenticationMethod}. Please contact your Identity Provider to reset your password.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst ldapIdentity = user.authIdentities?.find((i) => i.providerType === 'ldap');\n\t\t\tif (!user.password || (ldapIdentity && user.disabled)) {\n\t\t\t\tthis.logger.debug(","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/cli/src/controllers/password-reset.controller.ts#L80-L116","documentation":"POST /rest/forgot-password is rejected for a non-global-owner user when the license's user seat quota is exhausted (`license.isWithinUsersLimit()` returns false). The owner is exempt. Mirrors `RESPONSE_ERROR_MESSAGES.USERS_QUOTA_REACHED` returned as 403 Forbidden.","triggerScenarios":"forgotPassword for a user whose role.slug !== GLOBAL_OWNER_ROLE.slug while `license.isWithinUsersLimit()` is false.","commonSituations":"License seats filled; trial or plan limit reached; license expired and fell back to a tiny seat count.","solutions":["Upgrade the license or deactivate inactive users to free a seat.","Have the global owner perform the reset (owner is exempt from the check).","Verify the license is active and the seat count in Settings > Licensing."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// For non-owner users, check license seat availability before submitting.\nif (me.role !== 'owner' && !license.withinUsersLimit) {\n  throw new Error('User seat quota reached — ask the owner to reset your password.');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await restApi.post('/forgot-password', { email });\n} catch (e) {\n  if (e.response?.status === 403 && /maximum number of users/i.test(e.response.data.message)) {\n    routeToOwnerReset('Seat quota reached — contact the instance owner.');\n  } else throw e;\n}","preventionTips":["Surface remaining seat count in the admin panel.","For quota-blocked resets, instruct the user to contact the global owner (who is exempt).","Monitor seat usage and prune inactive users before the quota is hit."],"tags":["license","quota","user-management","password-reset"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}