PostgreSQL Error 40P01: Deadlock Detected

PostgreSQL error 40P01 (Deadlock Detected) explained: what it means, why it happens, and how to fix it — with copy-paste code examples.

SQLSTATE 40P01 PostgreSQL Last verified 2026-08-19

Quick Answer

Check the server log for the deadlock detail showing the two blocked statements. If that does not apply, ensure all code paths lock rows in a consistent global order — the full checklist is below.

Error Code

SQLSTATE: 40P01
Official name: Deadlock Detected
Service: PostgreSQL

What does this error mean?

The transaction was aborted because it was selected as the deadlock victim to break a deadlock cycle.

Common Causes

How to Fix

  1. Check the server log for the deadlock detail showing the two blocked statements
  2. Ensure all code paths lock rows in a consistent global order
  3. Keep transactions short and avoid holding locks across user input waits
  4. Add retry-with-jitter around transactions that may deadlock
  5. Consider SELECT ... FOR UPDATE ordering or advisory locks for hot rows

Code Examples

Consistent lock ordering sql
-- Always lock parent rows before children, in id order:
SELECT id FROM accounts WHERE id = ANY($1) ORDER BY id FOR UPDATE;

Ordering locks by a stable key (e.g. id) across all transactions prevents the cycles that cause deadlock 40P01.

Framework-Specific Fixes

django

Wrap in transaction.atomic() and retry on OperationalError caused by deadlock.

from django.db import transaction, OperationalError

@transaction.atomic
for attempt in range(5):
    try:
        transfer()
        break
    except OperationalError:
        if attempt == 4: raise
        continue
sqlalchemy

Use a retry loop catching OperationalError.isinstance for code 40P01.

from sqlalchemy.exc import OperationalError

for attempt in range(5):
    try:
        session.execute(stmt)
        session.commit()
        break
    except OperationalError as e:
        if e.orig.sqlstate == '40P01':
            session.rollback(); continue
        raise
rails

Use ActiveRecord's retriable? with a retry block around the transaction.

ActiveRecord::Base.transaction do
  # work
end.recoverable?  # then retry

You Might Also Like

Frequently Asked Questions

Why am I seeing PostgreSQL error 40P01?

Most often this happens when two transactions lock resources in opposite orders, forming a cycle, or when locking rows in application-defined order that differs across requests.

How do I fix PostgreSQL error 40P01?

Check the server log for the deadlock detail showing the two blocked statements.

Which frameworks have documented fixes for error 40P01?

This page documents fixes for: django, sqlalchemy, rails.

Official Sources

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