medusajs/medusa · error · MedusaError

NOT_ALLOWED

NOT_ALLOWED

Error message

Recovery codes require an enabled MFA factor

What it means

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).

Source

Thrown at packages/medusa/src/api/auth/mfa/recovery-codes/route.ts:23

import { IAuthModuleService } from "@medusajs/framework/types"
import { AuthEvents, MedusaError, Modules } from "@medusajs/framework/utils"
import { AuthMfaGenerateRecoveryCodesRequestType } from "../../validators"

/**
 * @since 2.15.3
 */
export const POST = async (
  req: AuthenticatedMedusaRequest<AuthMfaGenerateRecoveryCodesRequestType>,
  res: MedusaResponse
) => {
  const authService = req.scope.resolve<IAuthModuleService>(Modules.AUTH)
  const factors = await authService.listAuthMfa({
    auth_identity_id: req.auth_context.auth_identity_id,
    status: "enabled",
  })

  if (!factors.length) {
    throw new MedusaError(
      MedusaError.Types.NOT_ALLOWED,
      "Recovery codes require an enabled MFA factor"
    )
  }

  const { codes } = await authService.generateAuthMfaRecoveryCodes({
    auth_identity_id: req.auth_context.auth_identity_id,
    count: req.validatedBody.count,
  })

  await req.scope.resolve(Modules.EVENT_BUS).emit({
    name: AuthEvents.MFA_RECOVERY_CODES_GENERATED,
    data: {
      auth_identity_id: req.auth_context.auth_identity_id,
      count: codes.length,
    },
  })

View on GitHub (pinned to 5e06e544a2)

Solutions

  1. Create and verify an MFA factor first (register the factor, confirm the OTP, so its status becomes enabled)
  2. Call the MFA factors list endpoint and confirm status=enabled before requesting recovery codes
  3. If factors exist but are pending, complete their verification flow
  4. Re-enable a disabled factor or create a new one
Defensive patterns

Strategy: validation

Validate before calling

const factors = await authService.listAuthMfa({ auth_identity_id, status: 'enabled' })
if (!factors.length) {
  throw new Error('Enable an MFA factor before requesting recovery codes')
}

Type guard

function hasEnabledFactor(factors: { status: string }[]): boolean {
  return factors.some((f) => f.status === 'enabled')
}

Try / catch

catch (e) { if (e.type === 'not_allowed' && /enabled MFA factor/.test(e.message)) startFactorEnrollment() else throw e }

Prevention

When it happens

Trigger: Calling recovery code generation before any MFA factor was created and enabled, or after all factors were disabled.

Common situations: 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.

Related errors


AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27). Data as JSON: /api/errors/136495f3de5ffbe6. Report an issue: GitHub.