How to fix Lock timeout exceeded in PostgreSQL

PostgreSQLINTERMEDIATEMEDIUMUpdated Dec 28, 2025ยท 218 views

PostgreSQL aborts statements that wait too long for locks when lock_timeout is configured. Adjust the timeout value or resolve blocking queries to fix this error.

Quick fix

Fixes Lock timeout exceeded

SHOW lock_timeout;
  1. Check current lock_timeout setting
  2. Identify blocking queries
  3. Terminate long-running blocking queries
  4. Increase lock_timeout if appropriate
postgresql โ€” error
SHOW lock_timeout;
Lock timeout exceeded

What this error means

The "Lock timeout exceeded" error occurs when a PostgreSQL statement fails to acquire a lock within the time specified by the lock_timeout parameter. This parameter sets a maximum duration for waiting on locks (table locks, row locks, index locks, etc.). When a statement exceeds this wait time, PostgreSQL aborts it to prevent indefinite blocking. Unlike deadlock_timeout which checks for circular lock dependencies, lock_timeout simply enforces a wall-clock time limit on lock acquisition attempts.

Typical symptoms

  • Statements fail with "Lock timeout exceeded" error message
  • Database operations timeout while waiting for locks on tables or rows
  • DDL operations (ALTER TABLE, CREATE INDEX) fail under concurrent load
  • Application receives timeout exceptions during concurrent database modifications
  • Lock contention occurs during heavy concurrent read/write operations

Common causes

  1. lock_timeout parameter is set too low for your workload
  2. Other transactions hold locks on resources for extended periods
  3. High concurrency with conflicting transactions accessing same tables
  4. Long-running queries or transactions blocking lock acquisition
  5. Missing indexes causing full table scans that hold locks longer
  6. Inefficient transaction isolation level causing excessive locking

How to fix "Lock timeout exceeded"

1Check current lock_timeout setting

Connect to your PostgreSQL database and view the current lock_timeout value:

sql
SHOW lock_timeout;

The default is 0 (disabled). If the value is non-zero, that setting is enforced.

2Identify blocking queries

Find which queries are holding locks and blocking others:

sql
SELECT
  blocked_locks.pid AS blocked_pid,
  blocked_activity.usename AS blocked_user,
  blocking_locks.pid AS blocking_pid,
  blocking_activity.usename AS blocking_user,
  blocked_activity.query AS blocked_statement,
  blocking_activity.query AS blocking_statement,
  blocked_activity.application_name AS blocked_application,
  blocking_activity.application_name AS blocking_application
FROM pg_catalog.pg_locks blocked_locks
JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_activity.pid = blocked_locks.pid
JOIN pg_catalog.pg_locks blocking_locks ON blocking_locks.locktype = blocked_locks.locktype
  AND blocking_locks.database IS NOT DISTINCT FROM blocked_locks.database
  AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation
  AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page
  AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple
  AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid
  AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid
  AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid
  AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid
  AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid
  AND blocking_locks.pid != blocked_locks.pid
JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid
WHERE NOT blocked_locks.granted;

This query shows which queries are blocked and which are causing the block.

3Terminate long-running blocking queries

If blocking queries are long-running and unnecessary, terminate them:

sql
SELECT pg_terminate_backend(pid) FROM pg_stat_activity
WHERE pid != pg_backend_pid()
  AND state = 'active'
  AND query_start < NOW() - INTERVAL '30 minutes';

Be cautious when terminating queries to avoid data corruption. Only terminate queries you own or have permission to terminate.

4Increase lock_timeout if appropriate

If the timeout is too aggressive for your workload, increase it at the session or role level:

sql
SET lock_timeout = '30s';

Or for a specific role:

sql
ALTER ROLE myuser SET lock_timeout = '30s';

Never set lock_timeout globally in postgresql.conf as it affects all sessions. Typical values range from 5-30 seconds depending on your workload.

5Optimize transactions to reduce lock duration

Restructure transactions to minimize lock holding time:

- Move read-only queries outside locked transactions
- Use shorter transactions with focused operations
- Lock tables in consistent order across all transactions
- Use SELECT FOR UPDATE with appropriate lock levels
- Add indexes on frequently locked columns to reduce full table scans

Example of restructured transaction:

sql
-- Bad: Long transaction holding locks
BEGIN;
SELECT * FROM large_table;
-- ... application processing ...
UPDATE small_table SET col = value;
COMMIT;

-- Good: Short transaction with lock
BEGIN;
UPDATE small_table SET col = value;
COMMIT;

Advanced notes

The lock_timeout parameter is different from deadlock_timeout and statement_timeout. The deadlock_timeout (default 1s) controls how long to wait before checking for deadlock conditions. The statement_timeout aborts entire statements after a time limit. For preventing long waits on locks, lock_timeout is the most direct solution. If you have statement_timeout set, ensure lock_timeout is not set to a larger value, as statement_timeout will trigger first. On heavily concurrent systems (many short transactions competing for same resources), consider increasing lock_timeout to allow legitimate wait times, while also investigating why locks are held so long. Row-level locking (SELECT FOR UPDATE) is generally preferable to table-level locking to reduce contention.

Frequently asked questions

What does "Lock timeout exceeded" mean?
The "Lock timeout exceeded" error occurs when a PostgreSQL statement fails to acquire a lock within the time specified by the lock_timeout parameter. ). When a statement exceeds this wait time, PostgreSQL aborts it to prevent indefinite blocking.
How do I fix "Lock timeout exceeded"?
PostgreSQL aborts statements that wait too long for locks when lock_timeout is configured. Adjust the timeout value or resolve blocking queries to fix this error.
What causes "Lock timeout exceeded"?
lock_timeout parameter is set too low for your workload Other transactions hold locks on resources for extended periods High concurrency with conflicting transactions accessing same tables
Is the "Lock timeout exceeded" error serious?
It is a medium-severity error โ€” more of a blocker than a risk. The steps above resolve it.

Official resources & further reading