medusajs/medusa · error · MedusaError
Invalid MFA challenge code
Error message
Invalid MFA challenge code
What it means
The submitted code doesn't match the current MFA challenge code and the attempt count is still below max_attempts. The attempt counter is persisted, then this NOT_ALLOWED error is thrown — one more failure may tip into the lockout message.
Solutions
- Have the user request/enter a fresh code and retry
- Show remaining attempts in the UI so users are careful before lockout
- Sync the authenticator device time
Defensive patterns
Strategy: retry
Try / catch
try { await verifyAuthMfaChallenge(...) } catch (e) { if (e.message === 'Invalid MFA challenge code') { /* re-prompt for code */ } throw e } Prevention
- Re-prompt for a fresh code on failure instead of resubmitting the same one
- Sync authenticator device time
When it happens
Trigger: verifyAuthMfaChallenge with an incorrect or expired code while attempts < max_attempts.
Common situations: Typo in the code, code refreshed between display and entry, device clock drift for TOTP, or UI resubmitting an old code after regeneration.
Related errors
- Invalid MFA verification code
- An active TOTP factor already exists for this auth identity
- Auth identity does not have any enabled MFA methods
- Disabled MFA factors cannot be verified
- INVALID_DATA
AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27).
Data as JSON: /api/errors/cae5c9ef3b1879e3.
Report an issue: GitHub.
Appendix: source
Thrown at packages/modules/auth/src/services/auth-module.ts:500
const valid = await this.authMfaProviderService_.verify(
data.method,
{
auth_identity_id: challenge.auth_identity_id!,
code: data.code,
},
sharedContext
)
if (!valid) {
const attempts = challenge.attempts + 1
await this.setMfaChallenge_({
...challenge,
attempts,
})
throw new MedusaError(
MedusaError.Types.NOT_ALLOWED,
attempts >= challenge.max_attempts
? "MFA challenge has too many failed attempts"
: "Invalid MFA challenge code"
)
}
const completedChallenge: AuthTypes.AuthMfaChallengeDTO = {
...challenge,
completed_at: new Date(),
}
await this.setMfaChallenge_(completedChallenge)
return completedChallenge
}
@InjectManager()View on GitHub (pinned to 5e06e544a2)