Prisma error P2002 (Unique Constraint Failed) explained: what it means, why it happens, and how to fix it — with copy-paste code examples.
Read error.meta.target to see exactly which field(s) violated the unique constraint. If that does not apply, decide the intended behavior: reject the write, update the existing row, or skip it — the full checklist is below.
Error code: P2002
Official name: Unique Constraint Failed
System name: Unique constraint failed
Service: Prisma
Unique constraint failed on the {constraint}
await prisma.user.create({ data: { email } }) runs during a double-submit: the user clicked Sign Up twice, the first request committed, and the second throws PrismaClientKnownRequestError: Unique constraint failed on the fields: (email), with code P2002. The meta.target field lists the exact constrained fields, and the same P2002 wraps SQLSTATE 23505 (Postgres) or 1062 (MySQL) whichever engine you run on.
Run these to confirm the cause before applying a fix — each command answers a specific question.
catch (e) {
if (e instanceof Prisma.PrismaClientKnownRequestError) {
console.log(e.code, e.meta?.target); // P2002 ['email']
}
}
meta.target names the constrained fields — branch your error message on it instead of parsing the raw message string.
model User {
id Int @id @default(autoincrement())
email String @unique
}
@unique (or @@unique([a, b]) composites) is what produces P2002 — the constraint derives from the model definition.
Expected duplicate (signup / retry)?
├─ catch P2002 → return a friendly 'already exists' response
│ if (e.code === 'P2002') return conflict('Email already registered')
└─ prefer the atomic upsert:
prisma.user.upsert({ where: { email },
create: { email }, update: { lastSeenAt: new Date() } })
Unexpected duplicate (seed / import)?
└─ inspect the existing row first:
prisma.user.findUnique({ where: { email } })
const user = await prisma.user.upsert({
where: { email: 'ada@example.com' },
update: { lastLoginAt: new Date() },
create: { email: 'ada@example.com' },
})
upsert() checks the unique field first and updates the existing row instead of throwing P2002.
try {
await prisma.user.create({ data: input })
} catch (e) {
if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === 'P2002') {
console.error('conflicting field:', e.meta.target)
}
}
e.meta.target names the unique field, which is what you want to surface in the API response.
Catch PrismaClientKnownRequestError in a global exception filter and map code P2002 to HTTP 409 Conflict with the conflicting field.
@Catch(Prisma.PrismaClientKnownRequestError)
export class PrismaExceptionFilter implements ExceptionFilter {
catch(e, host) {
if (e.code === 'P2002') {
return response.status(409).json({
message: `Duplicate value on ${e.meta.target}`,
})
}
throw e
}
}
Wrap create/update calls in a helper that translates P2002 into a 409 response so the API never crashes on duplicates.
try {
await prisma.user.create({ data: { email } })
} catch (e) {
if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === 'P2002') {
return res.status(409).json({ error: 'Email already exists' })
}
throw e
}
Prefer upsert() over create() whenever the operation must be idempotent on a unique field.
await prisma.user.upsert({
where: { email },
update: { name },
create: { email, name },
})
Most often this happens when creating or updating a record whose value collides with an existing row on a unique field (email, username, slug, external id), or when using create() where an upsert() would be correct, so the second request fails on the unique index.
Read error.meta.target to see exactly which field(s) violated the unique constraint.
This page documents fixes for: nestjs, express, prisma-client.
Recommendations are editorial — DB Error Reference takes no payment or affiliate fees for tool listings.
This page is based on the official Prisma documentation linked below and adds practical troubleshooting guidance on top.