RocketChat/Rocket.Chat · error · Meteor.Error
totp-max-attempts
totp-max-attempts
Error message
TOTP Maximun Failed Attempts Reached
What it means
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.
Source
Thrown at apps/meteor/server/api/v1/twoFactorChallenges.ts:88
}
const { userId } = challenge;
const user = await getUserForCheck(userId);
if (!user) {
throw new Meteor.Error('error-user-not-found', 'user not found');
}
const twoFAMethod = getTwoFAMethodForOAuth(challenge.method);
const isCodeValid = await twoFAMethod.verifyEmailTwoFactorChallenge(user, challengeId, code);
if (!isCodeValid) {
const tooManyAttempts = await twoFAMethod.maxFaildedAttemtpsReached(user);
if (tooManyAttempts) {
await TwoFactorChallenges.removeByPendingChallengeId(challengeId);
throw new Meteor.Error('totp-max-attempts', 'TOTP Maximun Failed Attempts Reached');
}
return API.v1.failure('error-invalid-code', 'Invalid code');
}
const stampedToken = Accounts._generateStampedLoginToken();
await Accounts._insertLoginToken(user._id, stampedToken);
const hashedToken = Accounts._hashLoginToken(stampedToken.token);
const connection = {
...generateConnection(this.requestIp, this.request.headers),
token: hashedToken,
} as unknown as IMethodConnection;
// remember the 2FA authorization for the next requests
await rememberAuthorizationByToken(hashedToken, user._id, connection);
View on GitHub (pinned to b2c16d5842)
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
Example fix
// before if (!resp.success) retryVerify(); // retries until lockout // after if (err?.error === 'totp-max-attempts') restartLoginFlow(); else if (resp.error === 'error-invalid-code') promptUserForNewCode();
Defensive patterns
Strategy: try-catch
Try / catch
catch (e) {
if (e?.error === 'totp-max-attempts') { showLockoutMessage(); return startOAuthLogin(); }
throw e;
}
// and for in-band failures: if (resp.success === false && resp.error === 'error-invalid-code') promptForNewCode(); Prevention
- 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
When it happens
Trigger: 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').
Common situations: 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.
Related errors
- error-parameter-required
- error-challenge-not-found
- error-challenge-expired
- error-invalid-challenge-method
- error-user-not-found
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/17572a96d298fb17.
Report an issue: GitHub.