RocketChat/Rocket.Chat · error · Meteor.Error

error-invalid-challenge-method

error-invalid-challenge-method

Error message

invalid challenge method

What it means

Thrown by POST twoFactorChallenges.sendEmailCode when the pending challenge's method is anything other than 'email'. sendEmailCode only knows how to send email codes; a challenge created for TOTP or another method cannot be serviced by it. Which method a challenge uses is decided by the user's configured 2FA during the OAuth login flow.

Source

Thrown at apps/meteor/server/api/v1/twoFactorChallenges.ts:34

		async post() {
			const { challengeId } = this.bodyParams;

			if (!challengeId) {
				throw new Meteor.Error('error-parameter-required', 'challengeId is required');
			}

			const challenge = await TwoFactorChallenges.findOneByPendingChallengeId(challengeId);

			if (!challenge) {
				throw new Meteor.Error('error-challenge-not-found', 'challenge not found');
			}

			if (challenge.expireAt && challenge.expireAt < new Date()) {
				throw new Meteor.Error('error-challenge-expired', 'challenge expired');
			}

			if (challenge.method !== 'email') {
				throw new Meteor.Error('error-invalid-challenge-method', 'invalid challenge method');
			}

			const { userId } = challenge;

			const user = await getUserForCheck(userId);

			if (!user) {
				throw new Meteor.Error('error-user-not-found', 'user not found');
			}

			await emailCheckForOAuth.sendEmailCode(user);

			return API.v1.success();
		},
	},
);

API.v1.addRoute(

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Branch on the method reported by the login/challenge response: show the email resend button only for method 'email'
  2. For TOTP challenges, direct the user to their authenticator app and use twoFactorChallenges.verifyChallenge with the 6-digit code
  3. Do not call sendEmailCode speculatively to 'detect' the method — inspect the challenge payload instead

Example fix

// before
await sdk.post('twoFactorChallenges.sendEmailCode', { challengeId }); // assumes email
// after
if (challenge.method === 'email') {
  await sdk.post('twoFactorChallenges.sendEmailCode', { challengeId });
} else {
  showAuthenticatorInput(); // TOTP path
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (challenge.method && challenge.method !== 'email') throw new Error(`sendEmailCode only handles email challenges, got ${challenge.method}`);

Type guard

const isEmailChallenge = (c: { method?: string }): boolean => c.method === 'email';

Try / catch

catch (e) { if (e?.error === 'error-invalid-challenge-method') renderTotpInput(); else throw e; }

Prevention

When it happens

Trigger: User's 2FA is TOTP and the client unconditionally calls sendEmailCode for every challenge; a hybrid flow where the challenge method is chosen server-side but the client assumes email; calling sendEmailCode with a challengeId obtained from a TOTP-gated OAuth login.

Common situations: Frontends hardcoding the email-code path after testing only against email-2FA accounts; workspaces where admins switched the test user from email codes to authenticator apps; clients not branching on the method field returned with the challenge.

Related errors


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