Prisma ORM Error P2002: Unique constraint failed
Prisma tried to write a value that already exists in a @unique or @@unique field (Postgres 23505 underneath). Catch P2002 and return 409, or use upsert.
Identify: does your log match?
PrismaClientKnownRequestError:
Invalid `prisma.user.create()` invocation in
/app/src/routes/signup.ts:18:34
15 const hash = await argon2.hash(body.password);
16
17 // create account
→ 18 const user = await prisma.user.create(
Unique constraint failed on the fields: (`email`)
code: 'P2002',
meta: { modelName: 'User', target: [ 'email' ] }- High
Duplicate signup or double submit
The user clicked twice or the client retried after a timeout. The first create already committed the email.
- Medium
findFirst then create race
Two requests both check for an existing row, find none, and both call create. Only the database constraint stops the second.
- Medium
Seed script re-run on populated DB
prisma db seed with create() instead of upsert() fails on the second run.
Fix
Catch P2002 and return a 409
const user = await prisma.user.create({ data: { email, name } }); return Response.json(user);
import { Prisma } from '@prisma/client'; try { const user = await prisma.user.create({ data: { email, name } }); return Response.json(user, { status: 201 }); } catch (e) { if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === 'P2002') { return Response.json({ error: 'Email already registered' }, { status: 409 }); } throw e; }
The unique index is the source of truth; mapping P2002 to 409 gives clients a clear, non-retryable answer.
Verify: reproduce it on purpose
$ node -e "const {PrismaClient}=require('@prisma/client');const p=new PrismaClient();(async()=>{await p.user.create({data:{email:'dup@x.io'}});await p.user.create({data:{email:'dup@x.io'}})})().catch(e=>console.log(e.code))"Run it against a dev environment to trigger P2002 on purpose and test your error handler.
Prevent
- Read e.meta.target to know which field collided.
- Disable the submit button while the request is in flight.
- Prefer upsert in seed scripts.
FAQ
Why is meta.target an index name instead of field names?
On some databases and older Prisma versions, target is the constraint name (e.g. User_email_key). Parse both forms or match on the substring.
Can upsert still throw P2002?
Yes, if Prisma falls back to a non-native upsert (select then insert) and two calls race. Retry once on P2002 in that case.
Does P2002 roll back my $transaction?
Yes. Any thrown error inside an interactive $transaction rolls back all prior writes in it.
Related errors
Last verified 2026-10-20 · Source: Prisma error reference P1000–P6010 · Official docs ↗ · ~12,100 searches/mo