{"record":{"id":"6aa07f1235866da2","repo":"medusajs/medusa","slug":"mfa-challenge-has-expired","errorCode":null,"errorMessage":"MFA challenge has expired","messagePattern":"MFA challenge has expired","errorType":"exception","errorClass":"MedusaError","httpStatus":403,"severity":"error","filePath":"packages/modules/auth/src/services/auth-module.ts","lineNumber":834,"sourceCode":"\n    delete serialized.provider_metadata\n\n    return serialized\n  }\n\n  protected assertMfaChallengeCanBeVerified_(\n    challenge: AuthTypes.AuthMfaChallengeDTO,\n    method: AuthTypes.AuthMfaChallengeMethod\n  ): void {\n    if (challenge.completed_at) {\n      throw new MedusaError(\n        MedusaError.Types.NOT_ALLOWED,\n        \"MFA challenge has already been completed\"\n      )\n    }\n\n    if (new Date(challenge.expires_at).getTime() <= Date.now()) {\n      throw new MedusaError(\n        MedusaError.Types.NOT_ALLOWED,\n        \"MFA challenge has expired\"\n      )\n    }\n\n    if (challenge.attempts >= challenge.max_attempts) {\n      throw new MedusaError(\n        MedusaError.Types.NOT_ALLOWED,\n        \"MFA challenge has too many failed attempts\"\n      )\n    }\n\n    if (!challenge.methods.includes(method)) {\n      throw new MedusaError(\n        MedusaError.Types.INVALID_DATA,\n        `MFA challenge does not support method \"${method}\"`\n      )\n    }","sourceCodeStart":816,"sourceCodeEnd":852,"githubUrl":"https://github.com/medusajs/medusa/blob/5e06e544a296b9033f20f71f11c559f81a0e5739/packages/modules/auth/src/services/auth-module.ts#L816-L852","documentation":"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.","triggerScenarios":"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.","commonSituations":"Idle users returning to a stale OTP form; backgrounded mobile app; test suites with artificial delays; retrying old challenges after cache restart.","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"],"exampleFix":"// before\nawait authModule.verifyAuthMfaChallenge({ challenge_id, method, body })\n// after\ntry { await authModule.verifyAuthMfaChallenge({ challenge_id, method, body }) }\ncatch (e) { if (/expired/.test(e.message)) return restartMfaFlow() ; throw e }","handlingStrategy":"fallback","validationCode":"const challenge = await authModule.retrieveAuthMfaChallenge(challengeId)\nif (new Date(challenge.expires_at).getTime() <= Date.now()) return restartMfaFlow()","typeGuard":"null","tryCatchPattern":"try { await verify() } catch (e) { if (/expired/.test(e.message)) return restartMfaFlow(); throw e }","preventionTips":["Submit verification promptly","Offer a resend/expired UX path"],"tags":["mfa","challenge","expired","auth-module"],"backgroundTag":"otp-challenge-expired","analyzedSha":"5e06e544a296b9033f20f71f11c559f81a0e5739","analyzedAt":"2026-08-27T07:24:39.599Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}