{"record":{"id":"3a8dae7e56a02a91","repo":"medusajs/medusa","slug":"recovery-code-is-invalid-or-already-used","errorCode":null,"errorMessage":"Recovery code is invalid or already used","messagePattern":"Recovery code is invalid or already used","errorType":"exception","errorClass":"MedusaError","httpStatus":403,"severity":"error","filePath":"packages/modules/auth/src/services/auth-module.ts","lineNumber":678,"sourceCode":"    data: AuthTypes.UseAuthMfaRecoveryCodeDTO,\n    @MedusaContext() sharedContext: Context = {}\n  ): Promise<void> {\n    return await this.useAuthMfaRecoveryCode_(data, sharedContext)\n  }\n\n  @InjectTransactionManager()\n  protected async useAuthMfaRecoveryCode_(\n    data: AuthTypes.UseAuthMfaRecoveryCodeDTO,\n    @MedusaContext() sharedContext: Context = {}\n  ): Promise<void> {\n    const valid = await this.authMfaProviderService_.verify(\n      \"recovery_code\",\n      data,\n      sharedContext\n    )\n\n    if (!valid) {\n      throw new MedusaError(\n        MedusaError.Types.NOT_ALLOWED,\n        \"Recovery code is invalid or already used\"\n      )\n    }\n  }\n\n  @InjectManager()\n  async requestAuthVerification(\n    data: AuthTypes.RequestAuthVerificationDTO,\n    @MedusaContext() sharedContext: Context = {}\n  ): Promise<AuthTypes.RequestAuthVerificationResponse> {\n    if (!data.code_provider) {\n      throw new MedusaError(\n        MedusaError.Types.INVALID_DATA,\n        \"Verification provider is required\"\n      )\n    }\n","sourceCodeStart":660,"sourceCodeEnd":696,"githubUrl":"https://github.com/medusajs/medusa/blob/5e06e544a296b9033f20f71f11c559f81a0e5739/packages/modules/auth/src/services/auth-module.ts#L660-L696","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nawait authModule.useAuthMfaRecoveryCode({ auth_identity_id: id, provider: 'email', body: { code } })\n// after\nconst code = rawCode.trim()\ntry {\n  const res = await authModule.useAuthMfaRecoveryCode({ auth_identity_id: id, provider: 'email', body: { code } })\n} catch (e) { /* mark code consumed UI-side, prompt for next code */ }","handlingStrategy":"try-catch","validationCode":"const code = rawCode.trim()\nif (!code) throw new Error('Code required')","typeGuard":"null","tryCatchPattern":"try { await authModule.useAuthMfaRecoveryCode(input) } catch (e) { if (e.type === 'not_allowed') promptNextCode(); else throw e }","preventionTips":["Mark codes consumed client-side after use","Clean and validate code input before submitting"],"tags":["mfa","recovery-codes","auth-module","not-allowed"],"backgroundTag":"invalid-verification-code","analyzedSha":"5e06e544a296b9033f20f71f11c559f81a0e5739","analyzedAt":"2026-08-27T07:24:39.599Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}