medusajs/medusa · error · MedusaError
MFA challenge has expired
Error message
MFA challenge has expired
What it means
Raised when verifying an MFA challenge whose expires_at timestamp is in the past. Challenges (from cache) have a limited TTL; an expired challenge must be re-created via a new authentication/MFA flow.
Source
Thrown at packages/modules/auth/src/services/auth-module.ts:834
delete serialized.provider_metadata
return serialized
}
protected assertMfaChallengeCanBeVerified_(
challenge: AuthTypes.AuthMfaChallengeDTO,
method: AuthTypes.AuthMfaChallengeMethod
): void {
if (challenge.completed_at) {
throw new MedusaError(
MedusaError.Types.NOT_ALLOWED,
"MFA challenge has already been completed"
)
}
if (new Date(challenge.expires_at).getTime() <= Date.now()) {
throw new MedusaError(
MedusaError.Types.NOT_ALLOWED,
"MFA challenge has expired"
)
}
if (challenge.attempts >= challenge.max_attempts) {
throw new MedusaError(
MedusaError.Types.NOT_ALLOWED,
"MFA challenge has too many failed attempts"
)
}
if (!challenge.methods.includes(method)) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`MFA challenge does not support method "${method}"`
)
}View on GitHub (pinned to 5e06e544a2)
Solutions
- Catch this error and restart the MFA flow to issue a fresh challenge
- Show a 'code expired, resend' UI when the challenge TTL lapses
- Submit verification promptly after the challenge is created
Example fix
// before
await authModule.verifyAuthMfaChallenge({ challenge_id, method, body })
// after
try { await authModule.verifyAuthMfaChallenge({ challenge_id, method, body }) }
catch (e) { if (/expired/.test(e.message)) return restartMfaFlow() ; throw e } Defensive patterns
Strategy: fallback
Validate before calling
const challenge = await authModule.retrieveAuthMfaChallenge(challengeId) if (new Date(challenge.expires_at).getTime() <= Date.now()) return restartMfaFlow()
Type guard
null
Try / catch
try { await verify() } catch (e) { if (/expired/.test(e.message)) return restartMfaFlow(); throw e } Prevention
- Submit verification promptly
- Offer a resend/expired UX path
When it happens
Trigger: User waits past the challenge TTL (e.g. 5 minutes) on the code-entry screen before submitting; clock differences; long-running tests that pause between create and verify.
Common situations: Idle users returning to a stale OTP form; backgrounded mobile app; test suites with artificial delays; retrying old challenges after cache restart.
Related errors
- MFA challenge has already been completed
- Auth identity does not have any enabled MFA methods
- MFA factor with id "${id}" was not found
- Recovery code count must be between 1 and 50
- Recovery code is invalid or already used
AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27).
Data as JSON: /api/errors/6aa07f1235866da2.
Report an issue: GitHub.