medusajs/medusa · error · MedusaError
An active TOTP factor already exists for this auth identity
Error message
An active TOTP factor already exists for this auth identity
What it means
The TOTP MFA provider enforces a single active TOTP factor per auth identity. Calling start() when an enabled or pending TOTP factor already exists throws NOT_ALLOWED.
Source
Thrown at packages/modules/auth/src/providers/mfa/totp.ts:84
async start(
data: AuthTypes.AuthMfaStartDTO,
sharedContext: Context = {}
): Promise<AuthTypes.AuthMfaStartResponse> {
const totpConfig = this.getTotpConfig_()
const issuer = data.issuer ?? totpConfig.issuer
const existingFactors = await this.authMfaFactorService_.list(
{
auth_identity_id: data.auth_identity_id,
provider: this.method,
status: ["pending", "enabled"],
},
{ select: ["id"] },
sharedContext
)
if (existingFactors.length) {
throw new MedusaError(
MedusaError.Types.NOT_ALLOWED,
"An active TOTP factor already exists for this auth identity"
)
}
const secret = generateTotpSecret()
const factor = await this.authMfaFactorService_.create(
{
auth_identity_id: data.auth_identity_id,
provider: this.method,
status: "pending",
provider_metadata: {
secret: encryptSecret(secret, this.getEncryptionKey_()),
issuer,
},
metadata: {
...(data.metadata ?? {}),
label: data.label ?? undefined,View on GitHub (pinned to 5e06e544a2)
Solutions
- Delete/disable the existing factor first (deleteAuthMfaFactor), then start again
- If the existing factor is pending and orphaned, remove it via its id before re-initiating
- Check listMfaFactors for the identity before showing the setup button
Example fix
// before
await authModule.startMfa('totp', { auth_identity_id: id })
// after
const factors = await authModuleService.listAuthMfaFactors(id)
for (const f of factors.filter((f) => f.provider === 'totp')) {
await authModuleService.deleteAuthMfaFactor(f.id)
}
await authModule.startMfa('totp', { auth_identity_id: id }) Defensive patterns
Strategy: validation
Validate before calling
const factors = await authModuleService.listAuthMfaFactors(identityId)
const hasActiveTotp = factors.some((f) => f.provider === 'totp' && f.status !== 'disabled')
if (!hasActiveTotp) await authModuleService.startMfa('totp', { auth_identity_id: identityId }) Type guard
const hasActiveTotp = (factors: { provider: string; status: string }[]) => factors.some((f) => f.provider === 'totp' && f.status !== 'disabled') Try / catch
try { await start() } catch (e) { if (e.message.includes('already exists')) { /* offer reset flow: delete then restart */ } throw e } Prevention
- Check existing factors before showing setup UI
- Clean up abandoned pending setups periodically
When it happens
Trigger: authModuleService.createAuthMfaFactor / provider start() for 'totp' on an identity that already has a TOTP factor in enabled or pending status — e.g. the user retries 'set up authenticator' without finishing or removing the previous setup.
Common situations: Abandoned pending setups left behind after the user never scanned/confirmed the QR code; UI allowing repeated clicks of 'add MFA' without checking existing factors.
Related errors
- Only TOTP MFA factors can be verified with this method
- Disabled MFA factors cannot be verified
- Invalid TOTP code
- Invalid MFA verification code
- INVALID_DATA
AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27).
Data as JSON: /api/errors/0213c1f30ab52eb1.
Report an issue: GitHub.