Prisma Error P2034: Transaction Write Conflict or Deadlock

Prisma error P2034 (Transaction Write Conflict or Deadlock) explained: what it means, why it happens, and how to fix it — with copy-paste code examples.

Error code P2034 Prisma Last verified 2026-08-19

Quick Answer

Wrap the interactive transaction body in a retry loop (3 attempts with backoff) - P2034 is transient by design. If that does not apply, keep transactions short: do validation and external calls before $transaction, not inside it — the full checklist is below.

Error Code

Error code: P2034
Official name: Transaction Write Conflict or Deadlock
Service: Prisma

What does this error mean?

Transaction failed due to a write conflict or a deadlock. Please retry your transaction

Common Causes

How to Fix

  1. Wrap the interactive transaction body in a retry loop (3 attempts with backoff) - P2034 is transient by design
  2. Keep transactions short: do validation and external calls before $transaction, not inside it
  3. Update rows in a consistent global order across the codebase to avoid deadlocks
  4. Use optimistic concurrency (a version/updatedAt field checked in where) instead of blind updates
  5. If retries keep failing, inspect pg_stat_activity for the conflicting sessions and long-held locks

Code Examples

Retry loop for transient conflicts typescript
for (let attempt = 0; attempt < 3; attempt++) {
  try {
    return await prisma.$transaction(async (tx) => {
      /* business logic */
    })
  } catch (e) {
    if (e.code !== 'P2034') throw e
    if (attempt === 2) throw e
    await new Promise((r) => setTimeout(r, 50 * 2 ** attempt))
  }
}

P2034 is the documented signal to retry - exponential backoff avoids thundering-herd retries.

Optimistic concurrency on update typescript
await prisma.post.updateMany({
  where: { id, version: expectedVersion },
  data: { title, version: { increment: 1 } },
})
// if count === 0 the row changed under you - reload and reapply

Guarding with a version column turns blind updates into detectable conflicts without DB-level retries.

Framework-Specific Fixes

nestjs

Retry P2034 inside a small exponential-backoff helper; treat it as transient, never as a permanent failure.

async function withRetry(fn, attempts = 3) {
  for (let i = 0; i < attempts; i++) {
    try {
      return await fn()
    } catch (e) {
      if (e.code !== 'P2034' || i === attempts - 1) throw e
      await delay(50 * 2 ** i)
    }
  }
}
prisma-client

Keep interactive transactions minimal and only touch the rows you must; external I/O belongs outside $transaction.

await prisma.$transaction(async (tx) => {
  // only DB work here
  await tx.account.update({ where: { id }, data: { balance: { decrement: amount } } })
  await tx.account.update({ where: { id: otherId }, data: { balance: { increment: amount } } })
})

You Might Also Like

Frequently Asked Questions

Why am I seeing Prisma error P2034?

Most often this happens when two interactive transactions update the same row concurrently (lost-update race), or when transactions acquiring locks in opposite orders on two tables (classic deadlock).

How do I fix Prisma error P2034?

Wrap the interactive transaction body in a retry loop (3 attempts with backoff) - P2034 is transient by design.

Which frameworks have documented fixes for error P2034?

This page documents fixes for: nestjs, prisma-client.

Official Sources

This page is based on the official Prisma documentation linked below and adds practical troubleshooting guidance on top.