Menu

PostgreSQL Error 55P03: Lock Not Available

Troubleshoot PostgreSQL SQLSTATE 55P03 by checking lock_timeout, NOWAIT clauses, and the sessions blocking a requested lock.

Posted on By
On this page

PostgreSQL SQLSTATE 55P03 is lock_not_available. It means the statement could not acquire a requested lock. Common causes include the session’s lock_timeout expiring or a locking clause such as FOR UPDATE NOWAIT refusing to wait. PostgreSQL lists the code in its error-code appendix.

Check whether lock_timeout or NOWAIT caused the error

In the same database session, check the active lock and statement timeouts:

SHOW lock_timeout;
SHOW statement_timeout;

lock_timeout only limits time spent waiting to acquire a lock. It applies separately to each lock acquisition attempt; 0 disables it. A NOWAIT clause instead fails immediately when a selected row cannot be locked. For example:

SELECT *
FROM orders
WHERE order_id = 42
FOR UPDATE NOWAIT;

If another transaction already holds a conflicting row lock, this statement reports an error instead of waiting. NOWAIT applies to the row-level lock; the query still takes the required table-level lock normally. See the PostgreSQL SELECT locking clause and client connection defaults.

Find sessions blocking lock acquisition

While the affected statement is waiting, inspect active lock waiters from a separate connection:

SELECT
  blocked.pid AS blocked_pid,
  blocked.application_name AS blocked_application,
  blocked.query_start AS blocked_since,
  blocker_pids.pid AS blocking_pid,
  blocking.application_name AS blocking_application,
  blocking.state AS blocking_state,
  blocking.xact_start AS blocking_transaction_started
FROM pg_stat_activity AS blocked
CROSS JOIN LATERAL unnest(pg_blocking_pids(blocked.pid)) AS blocker_pids(pid)
LEFT JOIN pg_stat_activity AS blocking
  ON blocking.pid = blocker_pids.pid
WHERE blocked.wait_event_type = 'Lock'
ORDER BY blocked.query_start;

pg_blocking_pids() returns the process IDs blocking a session from acquiring a lock. A blocker may be a transaction that is idle but still open, not a query that is actively consuming CPU. A PID of 0 represents a prepared transaction; check pg_prepared_xacts for its transaction ID, owner, and preparation time. Check the application’s transaction boundaries and xact_start; see the official pg_blocking_pids documentation.

Do not terminate a blocking backend automatically. First identify its application and transaction owner, then decide whether it is safe to let it finish, roll back its transaction, or cancel it through the responsible application or an authorized administrator.

Set a timeout that matches the workload

For a session-specific limit, use a value suited to the operation:

SET lock_timeout = '1s';

If statement_timeout is also set, PostgreSQL recommends keeping lock_timeout lower; otherwise the statement timeout may fire first. Avoid setting a global lock timeout in postgresql.conf unless the same limit is appropriate for every session. Check database, role, connection-startup, and session settings when the value is unexpected.

A session-level SET remains active on that connection. In a pooled application, use SET LOCAL inside a transaction when the timeout should apply only to that transaction, or reset a session-level setting before returning the connection to the pool.

An explicit transaction is left in the failed state after the lock error. Roll it back, or roll back to a savepoint, before continuing; otherwise later statements report Error 25P02. If the error is SQLSTATE 57014 instead, the statement was canceled for a broader reason such as statement_timeout or a client cancel request; see PostgreSQL Error 57014.

For other SQLSTATE guides, browse PostgreSQL Error Troubleshooting.