How to fix Skipping vacuum of "table" --- lock not available in PostgreSQL
This message appears when PostgreSQL's autovacuum cannot acquire the required lock on a table. Usually harmless if occasional, but frequent occurrences indicate long-running transactions or conflicting operations blocking maintenance.
Quick fix
Fixes Skipping vacuum of "table" --- lock not available
SELECT schemaname, relname, last_vacuum, last_autovacuum
FROM pg_stat_user_tables
WHERE last_autovacuum IS NULL OR last_autovacuum < now() - interval '1 hour';- Identify tables with frequent skipping
- Find blocking locks using pg_locks
- Close long-running transactions
- Disable TRUNCATE option for problematic tables (PostgreSQL 9.4+)
SELECT schemaname, relname, last_vacuum, last_autovacuumFROM pg_stat_user_tablesWHERE last_autovacuum IS NULL OR last_autovacuum < now() - interval '1 hour';Skipping vacuum of "table" --- lock not available
What this error means
PostgreSQL autovacuum regularly runs VACUUM operations to reclaim space and maintain table health. The VACUUM command requires a ShareUpdateExclusiveLock on regular operations or AccessExclusiveLock on VACUUM FULL. When autovacuum attempts to lock a table but finds a conflicting lock already held, it skips that table with this message. This is a protective mechanism—autovacuum prefers to skip a table temporarily rather than wait indefinitely for locks. The log level is LOG (not WARNING or ERROR), making this a routine operational event.
Typical symptoms
- VACUUM skipping message appears in PostgreSQL logs
- Autovacuum tasks complete quickly but some tables are not processed
- Happens frequently for specific tables during peak usage times
- Tables grow larger than expected over time
Common causes
- Long-running transactions with open cursors holding locks on the table
- Concurrent DDL operations (ALTER TABLE, CREATE INDEX, TRUNCATE, CLUSTER) on the same table
- Another VACUUM FULL operation already running on that table
- VACUUM with TRUNCATE option enabled (default) cannot release space when locks conflict
- Hot standby replication with queries on standby servers holding locks while primary tries to vacuum
How to fix "Skipping vacuum of "table" --- lock not available"
Enable log_autovacuum_min_duration if not already set. Monitor PostgreSQL logs for which tables are frequently skipped. Use:
SELECT schemaname, relname, last_vacuum, last_autovacuum
FROM pg_stat_user_tables
WHERE last_autovacuum IS NULL OR last_autovacuum < now() - interval '1 hour';While the message appears, query the system views to identify what holds locks:
SELECT pid, usename, application_name, state, query
FROM pg_stat_activity
WHERE pid IN (
SELECT pid FROM pg_locks
WHERE relation = 'your_table'::regclass AND NOT granted
);This shows connections that are holding or waiting for locks.
Identify and close idle transactions holding locks. In your application:
- Use connection pooling (PgBouncer, pgpool) to prevent connection leaks
- Set idle_in_transaction_session_timeout (PostgreSQL 13+): ALTER SYSTEM SET idle_in_transaction_session_timeout = '5min';
- Close cursor-holding connections that are no longer needed
For already-stuck connections, carefully use:
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE pid != pg_backend_pid() AND state = 'idle in transaction';If VACUUM TRUNCATE is causing lock conflicts, disable it:
ALTER TABLE your_table SET (vacuum_truncate = off);This prevents VACUUM from trying to return disk space to the OS, which requires an exclusive lock. Space is still reclaimed for reuse within PostgreSQL, just not returned to the OS.
If you run manual VACUUM commands, use the SKIP_LOCKED option:
VACUUM (SKIP_LOCKED) your_table;This tells VACUUM to skip tables that have conflicting locks instead of waiting. Works for both VACUUM and ANALYZE.
Set appropriate timeouts to prevent vacuum from waiting forever:
ALTER SYSTEM SET lock_timeout = '10s';
ALTER SYSTEM SET statement_timeout = '30s';
SELECT pg_reload_conf();These limits prevent both user queries and maintenance tasks from blocking indefinitely.
Advanced notes
VACUUM FULL and TRUNCATE operations require exclusive locks (AccessExclusiveLock) and are more likely to conflict. Prefer regular VACUUM (ShareUpdateExclusiveLock) which allows concurrent reads and writes. On high-concurrency systems, increase autovacuum workers (autovacuum_max_workers) but reduce cost parameters (autovacuum_vacuum_cost_limit, autovacuum_vacuum_cost_delay) to make workers yield more frequently. For hot standby setups, queries on standby servers can block primary-side vacuum; use max_standby_streaming_delay to force standby queries to cancel if they hold locks too long. Consider partitioning large tables so autovacuum can process partitions independently.
Frequently asked questions
- What does "Skipping vacuum of "table" --- lock not available" mean?
- PostgreSQL autovacuum regularly runs VACUUM operations to reclaim space and maintain table health. The VACUUM command requires a ShareUpdateExclusiveLock on regular operations or AccessExclusiveLock on VACUUM FULL. When autovacuum attempts to lock a table but finds a conflicting lock already held, it skips that table with this message.
- How do I fix "Skipping vacuum of "table" --- lock not available"?
- This message appears when PostgreSQL's autovacuum cannot acquire the required lock on a table. Usually harmless if occasional, but frequent occurrences indicate long-running transactions or conflicting operations blocking maintenance.
- What causes "Skipping vacuum of "table" --- lock not available"?
- Long-running transactions with open cursors holding locks on the table Concurrent DDL operations (ALTER TABLE, CREATE INDEX, TRUNCATE, CLUSTER) on the same table Another VACUUM FULL operation already running on that table
- Is the "Skipping vacuum of "table" --- lock not available" error serious?
- It is a low-severity error — more of a blocker than a risk. The steps above resolve it.
Official resources & further reading
- 📚PostgreSQL Official Documentation
- 📖PostgreSQL VACUUM Documentation(Official Docs)