{"record":{"id":"17572a96d298fb17","repo":"RocketChat/Rocket.Chat","slug":"totp-max-attempts","errorCode":"totp-max-attempts","errorMessage":"TOTP Maximun Failed Attempts Reached","messagePattern":"TOTP Maximun Failed Attempts Reached","errorType":"exception","errorClass":"Meteor.Error","httpStatus":400,"severity":"error","filePath":"apps/meteor/server/api/v1/twoFactorChallenges.ts","lineNumber":88,"sourceCode":"\t\t\t}\n\n\t\t\tconst { userId } = challenge;\n\n\t\t\tconst user = await getUserForCheck(userId);\n\n\t\t\tif (!user) {\n\t\t\t\tthrow new Meteor.Error('error-user-not-found', 'user not found');\n\t\t\t}\n\n\t\t\tconst twoFAMethod = getTwoFAMethodForOAuth(challenge.method);\n\n\t\t\tconst isCodeValid = await twoFAMethod.verifyEmailTwoFactorChallenge(user, challengeId, code);\n\n\t\t\tif (!isCodeValid) {\n\t\t\t\tconst tooManyAttempts = await twoFAMethod.maxFaildedAttemtpsReached(user);\n\t\t\t\tif (tooManyAttempts) {\n\t\t\t\t\tawait TwoFactorChallenges.removeByPendingChallengeId(challengeId);\n\t\t\t\t\tthrow new Meteor.Error('totp-max-attempts', 'TOTP Maximun Failed Attempts Reached');\n\t\t\t\t}\n\t\t\t\treturn API.v1.failure('error-invalid-code', 'Invalid code');\n\t\t\t}\n\n\t\t\tconst stampedToken = Accounts._generateStampedLoginToken();\n\n\t\t\tawait Accounts._insertLoginToken(user._id, stampedToken);\n\n\t\t\tconst hashedToken = Accounts._hashLoginToken(stampedToken.token);\n\n\t\t\tconst connection = {\n\t\t\t\t...generateConnection(this.requestIp, this.request.headers),\n\t\t\t\ttoken: hashedToken,\n\t\t\t} as unknown as IMethodConnection;\n\n\t\t\t// remember the 2FA authorization for the next requests\n\t\t\tawait rememberAuthorizationByToken(hashedToken, user._id, connection);\n","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/api/v1/twoFactorChallenges.ts#L70-L106","documentation":"Thrown by POST twoFactorChallenges.verifyChallenge when the submitted code fails verification AND the user has reached the maximum failed-attempt count. The server deletes the pending challenge as a lockout measure, so the user cannot retry with the same challenge — the OAuth login must start over. Note the typo 'Maximun' is in the shipped message.","triggerScenarios":"Repeated wrong codes from an out-of-sync authenticator or stale email code; brute-force probing; sharing one challenge across tabs where each tab burns attempts. The preceding response for earlier wrong codes is the softer API.v1.failure('error-invalid-code').","commonSituations":"Users pasting an older email code after requesting a resend (resend invalidates prior codes); authenticator clock drift causing every TOTP to be 'wrong'; clients that auto-retry on failure and rapidly exhaust attempts.","solutions":["Handle totp-max-attempts as terminal: restart the whole OAuth login to get a new challenge","Distinguish error-invalid-code (try again, attempts remain) from totp-max-attempts (locked) in client code","Fix the root cause before retrying: resend a fresh email code, or resync the authenticator app"],"exampleFix":"// before\nif (!resp.success) retryVerify(); // retries until lockout\n// after\nif (err?.error === 'totp-max-attempts') restartLoginFlow();\nelse if (resp.error === 'error-invalid-code') promptUserForNewCode();","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"catch (e) {\n  if (e?.error === 'totp-max-attempts') { showLockoutMessage(); return startOAuthLogin(); }\n  throw e;\n}\n// and for in-band failures: if (resp.success === false && resp.error === 'error-invalid-code') promptForNewCode();","preventionTips":["Only retry on error-invalid-code and only with a freshly obtained code","Treat totp-max-attempts as terminal for that challenge","Do not auto-retry verification with the same code"],"tags":["rest-api","two-factor","oauth","rate-limit","authentication","lockout"],"backgroundTag":"two-factor-attempt-limit","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}