medusajs/medusa · error · MedusaError
Recovery code is invalid or already used
Error message
Recovery code is invalid or already used
What it means
Thrown by useAuthMfaRecoveryCode when the provider's verify call for the "recovery_code" method returns false — the code is unknown, malformed, or has already been consumed (recovery codes are single-use).
Source
Thrown at packages/modules/auth/src/services/auth-module.ts:678
data: AuthTypes.UseAuthMfaRecoveryCodeDTO,
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
return await this.useAuthMfaRecoveryCode_(data, sharedContext)
}
@InjectTransactionManager()
protected async useAuthMfaRecoveryCode_(
data: AuthTypes.UseAuthMfaRecoveryCodeDTO,
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
const valid = await this.authMfaProviderService_.verify(
"recovery_code",
data,
sharedContext
)
if (!valid) {
throw new MedusaError(
MedusaError.Types.NOT_ALLOWED,
"Recovery code is invalid or already used"
)
}
}
@InjectManager()
async requestAuthVerification(
data: AuthTypes.RequestAuthVerificationDTO,
@MedusaContext() sharedContext: Context = {}
): Promise<AuthTypes.RequestAuthVerificationResponse> {
if (!data.code_provider) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Verification provider is required"
)
}
View on GitHub (pinned to 5e06e544a2)
Solutions
- After a failed attempt, show a fresh code entry (codes are single-use)
- Confirm the recovery codes were generated for the same auth identity and were not regenerated since
- Parse/clean user input (trim whitespace, preserve case) before submitting
Example fix
// before
await authModule.useAuthMfaRecoveryCode({ auth_identity_id: id, provider: 'email', body: { code } })
// after
const code = rawCode.trim()
try {
const res = await authModule.useAuthMfaRecoveryCode({ auth_identity_id: id, provider: 'email', body: { code } })
} catch (e) { /* mark code consumed UI-side, prompt for next code */ } Defensive patterns
Strategy: try-catch
Validate before calling
const code = rawCode.trim()
if (!code) throw new Error('Code required') Type guard
null
Try / catch
try { await authModule.useAuthMfaRecoveryCode(input) } catch (e) { if (e.type === 'not_allowed') promptNextCode(); else throw e } Prevention
- Mark codes consumed client-side after use
- Clean and validate code input before submitting
When it happens
Trigger: Calling useAuthMfaRecoveryCode with a code that was already redeemed; typo'd or truncated code; re-submitting the same recovery code after a partially failed login flow; wrong auth identity.
Common situations: User retries login with the same recovery code after a later step failed; codes regenerated so old ones are invalid; client storing/displaying codes with whitespace loss or case changes.
Related errors
- Recovery code count must be between 1 and 50
- NOT_ALLOWED
- MFA factor with id "${id}" was not found
- MFA challenge has already been completed
- MFA challenge has expired
AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27).
Data as JSON: /api/errors/3a8dae7e56a02a91.
Report an issue: GitHub.