{"record":{"id":"136495f3de5ffbe6","repo":"medusajs/medusa","slug":"not-allowed-136495","errorCode":"NOT_ALLOWED","errorMessage":"Recovery codes require an enabled MFA factor","messagePattern":"Recovery codes require an enabled MFA factor","errorType":"exception","errorClass":"MedusaError","httpStatus":403,"severity":"error","filePath":"packages/medusa/src/api/auth/mfa/recovery-codes/route.ts","lineNumber":23,"sourceCode":"import { IAuthModuleService } from \"@medusajs/framework/types\"\nimport { AuthEvents, MedusaError, Modules } from \"@medusajs/framework/utils\"\nimport { AuthMfaGenerateRecoveryCodesRequestType } from \"../../validators\"\n\n/**\n * @since 2.15.3\n */\nexport const POST = async (\n  req: AuthenticatedMedusaRequest<AuthMfaGenerateRecoveryCodesRequestType>,\n  res: MedusaResponse\n) => {\n  const authService = req.scope.resolve<IAuthModuleService>(Modules.AUTH)\n  const factors = await authService.listAuthMfa({\n    auth_identity_id: req.auth_context.auth_identity_id,\n    status: \"enabled\",\n  })\n\n  if (!factors.length) {\n    throw new MedusaError(\n      MedusaError.Types.NOT_ALLOWED,\n      \"Recovery codes require an enabled MFA factor\"\n    )\n  }\n\n  const { codes } = await authService.generateAuthMfaRecoveryCodes({\n    auth_identity_id: req.auth_context.auth_identity_id,\n    count: req.validatedBody.count,\n  })\n\n  await req.scope.resolve(Modules.EVENT_BUS).emit({\n    name: AuthEvents.MFA_RECOVERY_CODES_GENERATED,\n    data: {\n      auth_identity_id: req.auth_context.auth_identity_id,\n      count: codes.length,\n    },\n  })\n","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/medusajs/medusa/blob/5e06e544a296b9033f20f71f11c559f81a0e5739/packages/medusa/src/api/auth/mfa/recovery-codes/route.ts#L5-L41","documentation":"Thrown by POST /auth/mfa/recovery-codes when the requesting auth identity has no MFA factor with status 'enabled'. Recovery codes are only generated as a backup for an active factor, so at least one enabled factor must exist first. Maps to HTTP 403 (NOT_ALLOWED).","triggerScenarios":"Calling recovery code generation before any MFA factor was created and enabled, or after all factors were disabled.","commonSituations":"Client calls the recovery-codes endpoint too early in onboarding; factor created but still in 'pending' status (not yet verified/enabled); factors disabled during account recovery.","solutions":["Create and verify an MFA factor first (register the factor, confirm the OTP, so its status becomes enabled)","Call the MFA factors list endpoint and confirm status=enabled before requesting recovery codes","If factors exist but are pending, complete their verification flow","Re-enable a disabled factor or create a new one"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const factors = await authService.listAuthMfa({ auth_identity_id, status: 'enabled' })\nif (!factors.length) {\n  throw new Error('Enable an MFA factor before requesting recovery codes')\n}","typeGuard":"function hasEnabledFactor(factors: { status: string }[]): boolean {\n  return factors.some((f) => f.status === 'enabled')\n}","tryCatchPattern":"catch (e) { if (e.type === 'not_allowed' && /enabled MFA factor/.test(e.message)) startFactorEnrollment() else throw e }","preventionTips":["Sequence onboarding: enroll factor → verify → generate recovery codes","Check factor status before calling recovery-codes endpoints"],"tags":["mfa","recovery-codes","auth"],"backgroundTag":"mfa-factor-not-enabled","analyzedSha":"5e06e544a296b9033f20f71f11c559f81a0e5739","analyzedAt":"2026-08-27T07:24:39.599Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}