Skip to content

fix(prisma-adapter): use deleteMany when deleting by non-unique field#8314

Merged
himself65 merged 1 commit intocanaryfrom
fix/prisma-delete-non-unique-field
Mar 3, 2026
Merged

fix(prisma-adapter): use deleteMany when deleting by non-unique field#8314
himself65 merged 1 commit intocanaryfrom
fix/prisma-delete-non-unique-field

Conversation

@himself65
Copy link
Copy Markdown
Contributor

Summary

  • Fixes Prisma adapter's delete() method to fall back to deleteMany() when the where clause doesn't contain an id field, since Prisma's delete() requires a WhereUniqueInput (unique/primary key)
  • This fixes the deleteVerificationByIdentifier flow where identifier is not a unique field on the verification table
  • Adds e2e adapter test to verify deletion by non-unique field works across all adapters

Closes #8313

Test plan

  • Added "delete - should delete by non-unique field" test in e2e/adapter/test/adapter-factory/basic.ts
  • Verified all memory adapter tests pass (412 passed, 1 skipped)
  • Verified lint and format checks pass

Prisma's delete() requires a WhereUniqueInput (unique/primary key field).
When deleting by non-unique fields like `identifier` on the verification
table, it throws a validation error. Fall back to deleteMany() when the
where clause does not contain an `id` field.

Closes #8313
@himself65 himself65 requested a review from Bekacru as a code owner March 3, 2026 04:26
Copilot AI review requested due to automatic review settings March 3, 2026 04:26
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 3, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Actions Updated (UTC)
better-auth-demo Ignored Ignored Mar 3, 2026 4:26am
better-auth-docs Skipped Skipped Mar 3, 2026 4:26am

Request Review

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Mar 3, 2026

Open in StackBlitz

@better-auth/api-key

npm i https://pkg.pr.new/@better-auth/api-key@8314

better-auth

npm i https://pkg.pr.new/better-auth@8314

auth

npm i https://pkg.pr.new/auth@8314

@better-auth/core

npm i https://pkg.pr.new/@better-auth/core@8314

@better-auth/drizzle-adapter

npm i https://pkg.pr.new/@better-auth/drizzle-adapter@8314

@better-auth/electron

npm i https://pkg.pr.new/@better-auth/electron@8314

@better-auth/expo

npm i https://pkg.pr.new/@better-auth/expo@8314

@better-auth/i18n

npm i https://pkg.pr.new/@better-auth/i18n@8314

@better-auth/kysely-adapter

npm i https://pkg.pr.new/@better-auth/kysely-adapter@8314

@better-auth/memory-adapter

npm i https://pkg.pr.new/@better-auth/memory-adapter@8314

@better-auth/mongo-adapter

npm i https://pkg.pr.new/@better-auth/mongo-adapter@8314

@better-auth/oauth-provider

npm i https://pkg.pr.new/@better-auth/oauth-provider@8314

@better-auth/passkey

npm i https://pkg.pr.new/@better-auth/passkey@8314

@better-auth/prisma-adapter

npm i https://pkg.pr.new/@better-auth/prisma-adapter@8314

@better-auth/redis-storage

npm i https://pkg.pr.new/@better-auth/redis-storage@8314

@better-auth/scim

npm i https://pkg.pr.new/@better-auth/scim@8314

@better-auth/sso

npm i https://pkg.pr.new/@better-auth/sso@8314

@better-auth/stripe

npm i https://pkg.pr.new/@better-auth/stripe@8314

@better-auth/telemetry

npm i https://pkg.pr.new/@better-auth/telemetry@8314

@better-auth/test-utils

npm i https://pkg.pr.new/@better-auth/test-utils@8314

commit: 77786db

@himself65 himself65 enabled auto-merge March 3, 2026 04:28
@himself65 himself65 added this pull request to the merge queue Mar 3, 2026
@himself65 himself65 removed this pull request from the merge queue due to a manual request Mar 3, 2026
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/prisma-adapter/src/prisma-adapter.ts">

<violation number="1" location="packages/prisma-adapter/src/prisma-adapter.ts:502">
P0: Empty `where` now falls back to `deleteMany({})`, which can delete all rows in the table.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

}
// Prisma's delete() requires a WhereUniqueInput (unique/primary key field).
// When deleting by non-unique fields (e.g. identifier), fall back to deleteMany.
const hasIdField = where?.some((w) => w.field === "id");
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0: Empty where now falls back to deleteMany({}), which can delete all rows in the table.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/prisma-adapter/src/prisma-adapter.ts, line 502:

<comment>Empty `where` now falls back to `deleteMany({})`, which can delete all rows in the table.</comment>

<file context>
@@ -497,6 +497,20 @@ export const prismaAdapter = (prisma: PrismaClient, config: PrismaConfig) => {
 					}
+					// Prisma's delete() requires a WhereUniqueInput (unique/primary key field).
+					// When deleting by non-unique fields (e.g. identifier), fall back to deleteMany.
+					const hasIdField = where?.some((w) => w.field === "id");
+					if (!hasIdField) {
+						const whereClause = convertWhereClause({
</file context>
Suggested change
const hasIdField = where?.some((w) => w.field === "id");
if (!where?.length) return;
const hasIdField = where.some((w) => w.field === "id");
Fix with Cubic

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a bug in the Prisma adapter where calling delete() with a non-unique field (like identifier on the verification table) causes a PrismaClientValidationError, because Prisma's delete() requires a WhereUniqueInput. The fix adds a fallback to deleteMany() when the where clause does not contain an id field. An e2e test is also added to verify the fix across all adapters.

Changes:

  • packages/prisma-adapter/src/prisma-adapter.ts: Adds a hasIdField guard in the delete() method to fall back to deleteMany() when no id field is present in the where clause.
  • e2e/adapter/test/adapter-factory/basic.ts: Adds a new test case "delete - should delete by non-unique field" using a verification record to verify the fix.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
packages/prisma-adapter/src/prisma-adapter.ts Adds deleteMany fallback in delete() when where clause lacks an id field
e2e/adapter/test/adapter-factory/basic.ts Adds e2e test for deletion via non-unique identifier field on the verification model

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +502 to +503
const hasIdField = where?.some((w) => w.field === "id");
if (!hasIdField) {
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hasIdField check (where?.some((w) => w.field === "id")) is overly broad. It falls back to deleteMany for any where clause that doesn't contain id, including cases where the where clause contains a genuinely unique field like session.token.

For example, deleteSession in internal-adapter.ts calls adapter.delete({ model: "session", where: [{ field: "token", value: token }] }). Since session.token is unique: true in the BetterAuth schema (and carries a @unique constraint in the Prisma schema), Prisma's delete() already accepted this before this change. After this change, that path silently switches to deleteMany.

A more precise fix would check whether the where-clause fields are actually unique (using getFieldAttributes, which is already available in this closure and returns unique: true for fields like session.token), and only fall back to deleteMany when none of the fields are unique. This would correctly identify identifier (non-unique, only has index: true) as needing deleteMany, while id and token (both unique) would continue to use delete.

Suggested change
const hasIdField = where?.some((w) => w.field === "id");
if (!hasIdField) {
const hasUniqueWhereField = where?.some((w) => {
const fieldAttributes = getFieldAttributes(model, w.field);
// Use delete() when at least one where field is unique or a primary key.
return !!fieldAttributes?.unique || !!fieldAttributes?.id;
});
if (!hasUniqueWhereField) {

Copilot uses AI. Check for mistakes.
@himself65 himself65 added this pull request to the merge queue Mar 3, 2026
Merged via the queue into canary with commit c9b9c91 Mar 3, 2026
29 checks passed
@better-auth better-auth locked as resolved and limited conversation to collaborators Apr 1, 2026
@bytaesu bytaesu added the locked Locked conversations after being closed for 7 days label Apr 1, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

locked Locked conversations after being closed for 7 days

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Prisma adapter: verification.delete() uses non-unique identifier in WhereUniqueInput

3 participants