fix(prisma-adapter): use deleteMany when deleting by non-unique field#8314
fix(prisma-adapter): use deleteMany when deleting by non-unique field#8314
Conversation
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
|
The latest updates on your projects. Learn more about Vercel for GitHub. 2 Skipped Deployments
|
@better-auth/api-key
better-auth
auth
@better-auth/core
@better-auth/drizzle-adapter
@better-auth/electron
@better-auth/expo
@better-auth/i18n
@better-auth/kysely-adapter
@better-auth/memory-adapter
@better-auth/mongo-adapter
@better-auth/oauth-provider
@better-auth/passkey
@better-auth/prisma-adapter
@better-auth/redis-storage
@better-auth/scim
@better-auth/sso
@better-auth/stripe
@better-auth/telemetry
@better-auth/test-utils
commit: |
There was a problem hiding this comment.
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"); |
There was a problem hiding this comment.
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>
| const hasIdField = where?.some((w) => w.field === "id"); | |
| if (!where?.length) return; | |
| const hasIdField = where.some((w) => w.field === "id"); |
There was a problem hiding this comment.
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 ahasIdFieldguard in thedelete()method to fall back todeleteMany()when noidfield is present in thewhereclause.e2e/adapter/test/adapter-factory/basic.ts: Adds a new test case"delete - should delete by non-unique field"using averificationrecord 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.
| const hasIdField = where?.some((w) => w.field === "id"); | ||
| if (!hasIdField) { |
There was a problem hiding this comment.
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.
| 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) { |
Summary
delete()method to fall back todeleteMany()when the where clause doesn't contain anidfield, since Prisma'sdelete()requires aWhereUniqueInput(unique/primary key)deleteVerificationByIdentifierflow whereidentifieris not a unique field on the verification tableCloses #8313
Test plan
e2e/adapter/test/adapter-factory/basic.ts