PostgreSQL error: canceling statement due to lock timeout
ERROR: canceling statement due to lock timeout
The statement waited for a lock longer than lock_timeout allows and gave up. Unlike most errors on this list, this one usually means a safety mechanism worked: failing fast on a lock is almost always better than queueing behind it — especially for DDL.
What this error means
Why failing fast matters is about the queue, not the waiter: an ALTER TABLE needs an ACCESS EXCLUSIVE lock. While it waits behind some long-running query, every new query on that table — including plain SELECTs — queues behind it. A stuck migration can freeze a production table within seconds. lock_timeout converts that scenario into a quick, retryable failure.
Do not confuse it with statement_timeout: that bounds total runtime; lock_timeout bounds a single lock wait and has its own SQLSTATE (55P03).
Common causes
- DDL behind a long transaction — a report, a stuck
idle in transactionsession, a batch job. - Row locks held long by transactions that do slow work between BEGIN and COMMIT.
- Lock queues: you were not waiting on the original holder but on the exclusive-lock waiter in front of you.
How to diagnose it
-- Chi blocca chi, adesso:
SELECT waiting.pid, waiting.query AS waiting_query,
blocking.pid AS blocking_pid, blocking.query AS blocking_query,
now() - blocking.xact_start AS blocking_xact_age
FROM pg_stat_activity waiting
JOIN pg_stat_activity blocking
ON blocking.pid = ANY (pg_blocking_pids(waiting.pid));
Also set log_lock_waits = on: waits longer than deadlock_timeout get logged with both sides, giving you a history of contention, not just the live view.
How to fix it
- For migrations, keep the pattern and add retries:
Short timeout, retry loop, off-peak window: the standard for zero-drama DDL.SET lock_timeout = '3s'; ALTER TABLE orders ADD COLUMN note text; -- se fallisce: riprova con backoff - Deal with the blocker: fix the job that holds transactions open; set
idle_in_transaction_session_timeout; as a deliberate last resort,pg_terminate_backend(pid). - Prefer non-blocking variants where they exist:
CREATE INDEX CONCURRENTLY, batched backfills instead of one giant UPDATE.
