Diagnostic Queries
Symptoms
A statement waited for a lock longer than lock_timeout and was cancelled. PostgreSQL raises SQLSTATE 55P03 (lock_not_available).
- Only happens when
lock_timeoutis set (default 0 = wait forever). - The statement is cancelled while blocked, not while running.
- Common guard for DDL that must not wait behind long transactions.
What the server log shows
ERROR: canceling statement due to lock timeout
Why PostgreSQL raises this — what the manual says
Section 19.11.1 Statement Behavior (lock_timeout):
“Abort any statement that waits longer than the specified amount of time while attempting to acquire a lock on a table, index, row, or other database object.”
While blocked waiting to acquire a lock, the backend checks elapsed wait time against lock_timeout. If it exceeds the limit, the wait is abandoned and the statement is cancelled with 55P03.
Common causes
- A long-running transaction holding a conflicting lock.
- DDL blocked behind active queries on the same table.
- An intentionally short
lock_timeoutguard around migrations.
Relevant GUC parameters
| Parameter | Default | Effect |
|---|---|---|
lock_timeout |
0 | Max time to wait for a lock before cancelling (0 = wait forever). |
deadlock_timeout |
1s | How long to wait before checking for a deadlock. |
How to fix it
- Find and resolve the blocking session (commit/cancel it).
- Retry the statement after the blocker releases its lock.
- Keep transactions short; run DDL during low-traffic windows with a sensible
lock_timeout.
Diagnostic query
-- Who holds the lock we are waiting on?
SELECT blocked.pid AS blocked_pid, blocking.pid AS blocking_pid,
blocking.query AS blocking_query
FROM pg_stat_activity blocked
JOIN pg_stat_activity blocking
ON blocking.pid = ANY(pg_blocking_pids(blocked.pid))
WHERE blocked.wait_event_type = 'Lock';
Related & next steps
Reference: PostgreSQL 18 Section 20.10 “Statement Behavior”.
Thanks — noted. This helps keep the database accurate.