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

  1. Handle totp-max-attempts as terminal: restart the whole OAuth login to get a new challenge
  2. Distinguish error-invalid-code (try again, attempts remain) from totp-max-attempts (locked) in client code
  3. 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

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


AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18). Data as JSON: /api/errors/17572a96d298fb17. Report an issue: GitHub.