Email OTP login triggers Prisma P2025 when deleting verification record #2381

Closed
opened 2026-03-13 09:48:30 -05:00 by GiteaMirror · 3 comments
Owner

Originally created by @phillipplum on GitHub (Nov 24, 2025).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Request an OTP for an existing email through the login flow.
  2. Enter the received OTP to finish the sign-in.
  3. Observe the API logs.

Current vs. Expected behavior

OTP login completes without errors and the associated verification entry is removed successfully.

What version of Better Auth are you using?

1.4.1

System info

{
  "system": {
    "platform": "darwin",
    "arch": "arm64",
    "version": "Darwin Kernel Version 25.0.0: Wed Sep 17 21:35:32 PDT 2025; root:xnu-12377.1.9~141/RELEASE_ARM64_T6020",
    "release": "25.0.0",
    "cpuCount": 12,
    "cpuModel": "Apple M2 Max",
    "totalMemory": "32.00 GB",
    "freeMemory": "1.02 GB"
  },
  "node": {
    "version": "v24.11.0",
    "env": "development"
  },
  "packageManager": {
    "name": "pnpm",
    "version": "10.20.0"
  },
  "frameworks": null,
  "databases": null,
  "betterAuth": {
    "version": "1.4.1",
    "config": null
  }
}

Which area(s) are affected? (Select all that apply)

Backend

Auth config (if applicable)

import { betterAuth } from "better-auth"
export const auth = betterAuth({
  basePath: '/auth',
  plugins: [emailOTP({ sendVerificationOTP: async ({ email, otp }) => { console.log(otp); }})]
  emailAndPassword: {  
    enabled: false
  },
});

Additional context

After completing the email OTP sign-in flow with better-auth, Prisma throws a P2025 error because the verification.delete() call does not find a matching record.

Originally created by @phillipplum on GitHub (Nov 24, 2025). ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce 1. Request an OTP for an existing email through the login flow. 2. Enter the received OTP to finish the sign-in. 3. Observe the API logs. ### Current vs. Expected behavior OTP login completes without errors and the associated verification entry is removed successfully. ### What version of Better Auth are you using? 1.4.1 ### System info ```bash { "system": { "platform": "darwin", "arch": "arm64", "version": "Darwin Kernel Version 25.0.0: Wed Sep 17 21:35:32 PDT 2025; root:xnu-12377.1.9~141/RELEASE_ARM64_T6020", "release": "25.0.0", "cpuCount": 12, "cpuModel": "Apple M2 Max", "totalMemory": "32.00 GB", "freeMemory": "1.02 GB" }, "node": { "version": "v24.11.0", "env": "development" }, "packageManager": { "name": "pnpm", "version": "10.20.0" }, "frameworks": null, "databases": null, "betterAuth": { "version": "1.4.1", "config": null } } ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth" export const auth = betterAuth({ basePath: '/auth', plugins: [emailOTP({ sendVerificationOTP: async ({ email, otp }) => { console.log(otp); }})] emailAndPassword: { enabled: false }, }); ``` ### Additional context After completing the email OTP sign-in flow with better-auth, Prisma throws a `P2025` error because the `verification.delete()` call does not find a matching record.
GiteaMirror added the bug label 2026-03-13 09:48:30 -05:00
Author
Owner

@better-auth-agent[bot] commented on GitHub (Nov 24, 2025):

Thanks for raising this issue! The Prisma P2025 error indicates that the deletion of the verification record failed because the record couldn’t be found. This may happen if the record was already deleted or maybe wasn’t created at all for that flow. In your email OTP flow, you could try the following troubleshooting steps:

• Verify that a verification record is indeed created for each OTP request.
• Confirm that the record’s identifier used for deletion matches the one in the database.
• Consider wrapping the deletion call in a try/catch block so that if the record isn’t found it’s handled gracefully rather than throwing an error.
• Check for potential race conditions where the record might be deleted by a parallel process.

These steps should help you pinpoint whether the issue is with record creation, identifier mismatches, or timing issues during deletion [1].

Tag @better-auth-agent if you would like an answer from the Agent. What additional details can you share about how you’re tracking the verification records?

DiagramDiscordGitHub

Diagram Join Star

@better-auth-agent[bot] commented on GitHub (Nov 24, 2025): Thanks for raising this issue! The Prisma P2025 error indicates that the deletion of the verification record failed because the record couldn’t be found. This may happen if the record was already deleted or maybe wasn’t created at all for that flow. In your email OTP flow, you could try the following troubleshooting steps: • Verify that a verification record is indeed created for each OTP request. • Confirm that the record’s identifier used for deletion matches the one in the database. • Consider wrapping the deletion call in a try/catch block so that if the record isn’t found it’s handled gracefully rather than throwing an error. • Check for potential race conditions where the record might be deleted by a parallel process. These steps should help you pinpoint whether the issue is with record creation, identifier mismatches, or timing issues during deletion [[1]](https://github.com/better-auth/better-auth/issues/4125). Tag @better-auth-agent if you would like an answer from the Agent. What additional details can you share about how you’re tracking the verification records? <!-- bot:webhook reply v1 --> [Diagram](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) • [Discord](https://discord.gg/better-auth) • [GitHub](https://github.com/better-auth/better-auth) [![Diagram](https://img.shields.io/badge/Diagram-2b3137?style=flat-square)](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) [![Join](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&style=flat-square)](https://discord.gg/better-auth) [![Star](https://img.shields.io/badge/star-181717?logo=github&logoColor=white&style=flat-square)](https://github.com/better-auth/better-auth)
Author
Owner

@dosubot[bot] commented on GitHub (Nov 24, 2025):

The Prisma P2025 error during email OTP login happens because of a race condition between the cleanup of expired verification records and the explicit deletion after OTP verification. The Better Auth email OTP flow deletes the verification record after a successful sign-in, but there's also a cleanup step that removes expired records. If the cleanup runs just before the explicit delete, the record is already gone, and Prisma throws P2025 since it can't find the row to delete. This is not a double-deletion in the code, but a timing issue between these two operations (source).

To work around this, you can catch and suppress the P2025 error when deleting the verification record, since the desired state (the record is gone) is already achieved. Making the deletion idempotent or updating the adapter to ignore "not found" errors would also prevent this from surfacing as an exception. This is a safe approach because the verification record is only meant to exist until the OTP is used or expires.

If you want to patch this in your own code, wrap the delete call in a try/catch and ignore P2025 errors. For maintainers, making the deletion operation idempotent or handling P2025 gracefully in the adapter would be a robust long-term fix.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other

Chat with Dosu Open in Cursor Join Discord Share on X

@dosubot[bot] commented on GitHub (Nov 24, 2025): <!-- Answer --> The Prisma P2025 error during email OTP login happens because of a race condition between the cleanup of expired verification records and the explicit deletion after OTP verification. The Better Auth email OTP flow deletes the verification record after a successful sign-in, but there's also a cleanup step that removes expired records. If the cleanup runs just before the explicit delete, the record is already gone, and Prisma throws P2025 since it can't find the row to delete. This is not a double-deletion in the code, but a timing issue between these two operations [(source)](https://github.com/better-auth/better-auth/blob/fc662c52dcadc9874d36bd354d53d23f8a24e098/packages/better-auth/src/plugins/email-otp/index.ts#L245-L928). To work around this, you can catch and suppress the P2025 error when deleting the verification record, since the desired state (the record is gone) is already achieved. Making the deletion idempotent or updating the adapter to ignore "not found" errors would also prevent this from surfacing as an exception. This is a safe approach because the verification record is only meant to exist until the OTP is used or expires. If you want to patch this in your own code, wrap the delete call in a try/catch and ignore P2025 errors. For maintainers, making the deletion operation idempotent or handling P2025 gracefully in the adapter would be a robust long-term fix. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/0765aced-16d4-44cf-a015-d6016549b1ac?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/0765aced-16d4-44cf-a015-d6016549b1ac?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/0765aced-16d4-44cf-a015-d6016549b1ac?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/0765aced-16d4-44cf-a015-d6016549b1ac?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/0765aced-16d4-44cf-a015-d6016549b1ac?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/0765aced-16d4-44cf-a015-d6016549b1ac?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/0765aced-16d4-44cf-a015-d6016549b1ac?feedback_type=other)</sup> [![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=The%20Prisma%20P2025%20error%20during%20email%20OTP%20login%20happens%20because%20of%20a%20race%20condition%20between%20the%20cleanup%20of%20expired%20verification%20records%20and%20the%20explicit%20deletion%20after%20OTP%20verification.%20The%20Better%20Auth%20email%20OTP%20flow%20deletes%20the%20verification%20record%20after%20a%20successful%20sign-in%2C%20but%20there%27s%20also%20a%20cleanup%20step%20that%20removes%20expired%20records.%20If%20the%20cleanup%20runs%20just%20before%20the%20explicit%20delete%2C%20the%20record%20is%20already%20gone%2C%20and%20Prisma%20throws%20P2025%20since%20it%20can%27t%20find%20the%20row%20to%20delete.%20This%20is%20not%20a%20double-deletion%20in%20the%20code%2C%20but%20a%20timing%20issue%20between%20these%20two%20operations%20%5B%28source%29%5D%28https%3A//github.com/better-auth/better-auth/blob/fc662c52dcadc9874d36bd354d53d23f8a24e098/packages/better-auth/src/plugins/email-otp/index.ts%23L245-L928%29.%0A%0ATo%20work%20around%20this%2C%20you%20can%20catch%20and%20suppress%20the%20P2025%20error%20when%20deleting%20the%20verification%20record%2C%20since%20the%20desired%20state%20%28the%20record%20is%20gone%29%20is%20already%20achieved.%20Making%20the%20deletion%20idempotent%20or%20updating%20the%20adapter%20to%20ignore%20%22not%20found%22%20errors%20would%20also%20prevent%20this%20from%20surfacing%20as%20an%20exception.%20This%20is%20a%20safe%20approach%20because%20the%20verification%20record%20is%20only%20meant%20to%20exist%20until%20the%20OTP%20is%20used%20or%20expires.%0A%0AIf%20you%20want%20to%20patch%20this%20in%20your%20own%20code%2C%20wrap%20the%20delete%20call%20in%20a%20try/catch%20and%20ignore%20P2025%20errors.%20For%20maintainers%2C%20making%20the%20deletion%20operation%20idempotent%20or%20handling%20P2025%20gracefully%20in%20the%20adapter%20would%20be%20a%20robust%20long-term%20fix.)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/6267)
Author
Owner

@phillipplum commented on GitHub (Nov 24, 2025):

The problem is not with better-auth, even though nothing in the client code was changed after the update to version 1.4.1.

The verification request was executed twice in the client.

@phillipplum commented on GitHub (Nov 24, 2025): The problem is not with better-auth, even though nothing in the client code was changed after the update to version 1.4.1. The verification request was executed twice in the client.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#2381