How to fix VACUUM cannot run inside a transaction block in PostgreSQL
VACUUM is a PostgreSQL maintenance command that cannot run inside a transaction block. This error occurs when your connection has an active transaction. Enable autocommit mode or commit before running VACUUM.
Quick fix
Fixes VACUUM cannot run inside a transaction block
import psycopg2
conn = psycopg2.connect("dbname=mydb user=postgres")
conn.autocommit = True
cur = conn.cursor()
cur.execute("VACUUM FULL ANALYZE")
conn.close()- Enable autocommit for your database connection
- Use psycopg2.extensions for finer control (Python)
- Commit active transactions before VACUUM
- Use raw connection for VACUUM in SQLAlchemy
import psycopg2conn = psycopg2.connect("dbname=mydb user=postgres")conn.autocommit = Truecur = conn.cursor()cur.execute("VACUUM FULL ANALYZE")conn.close()VACUUM cannot run inside a transaction block
What this error means
PostgreSQL requires VACUUM to run in autocommit mode because it performs maintenance operations that cannot be rolled back. The VACUUM command removes dead rows and optimizes table storage. When your database connection has an active transaction (the default in most ORMs and drivers), PostgreSQL blocks VACUUM to prevent inconsistent states. This is by design and not a bug—VACUUM is a non-transactional operation.
Typical symptoms
- VACUUM fails with "ERROR: VACUUM cannot run inside a transaction block"
- VACUUM ANALYZE or VACUUM FULL commands fail the same way
- Running VACUUM in your application or script produces the error
- psycopg2, SQLAlchemy, or other database drivers fail when executing VACUUM
- Scheduled maintenance jobs cannot execute VACUUM operations
Common causes
- Your database connection has an active transaction (autocommit is disabled)
- Using an ORM or connection pool that defaults to autocommit=false
- Running VACUUM as part of a multi-statement query block
- Connection isolation level is not set to autocommit
- VACUUM is mixed with other SQL commands in the same transaction
How to fix "VACUUM cannot run inside a transaction block"
The most straightforward fix is to enable autocommit mode before executing VACUUM.
For psycopg2 (Python):
import psycopg2
conn = psycopg2.connect("dbname=mydb user=postgres")
conn.autocommit = True
cur = conn.cursor()
cur.execute("VACUUM FULL ANALYZE")
conn.close()For psycopg3 (newer Python driver):
import psycopg
conn = psycopg.connect("dbname=mydb user=postgres", autocommit=True)
conn.execute("VACUUM FULL ANALYZE")
conn.close()If you need more control over isolation levels:
import psycopg2
import psycopg2.extensions
conn = psycopg2.connect("dbname=mydb user=postgres")
conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
cur = conn.cursor()
cur.execute("VACUUM FULL ANALYZE")
conn.close()This explicitly sets the connection to autocommit mode.
If you cannot enable autocommit, commit any pending transactions first:
COMMIT;
VACUUM FULL ANALYZE;Or in application code:
# Using SQLAlchemy
from sqlalchemy import text
with engine.connect() as conn:
conn.commit() # Commit any pending transaction
conn.execute(text("VACUUM FULL ANALYZE"))
conn.commit()Note: SQLAlchemy may still wrap this in a transaction. See the next step for a better approach.
With SQLAlchemy, bypass the ORM transaction handling:
from sqlalchemy import create_engine, text
engine = create_engine("postgresql://user:password@localhost/dbname")
# Get a raw DBAPI connection with autocommit
raw_conn = engine.raw_connection()
raw_conn.autocommit = True
cur = raw_conn.cursor()
cur.execute("VACUUM FULL ANALYZE")
raw_conn.close()This bypasses SQLAlchemy's transaction handling entirely.
In production, avoid running VACUUM from your application. Instead:
1. Use autovacuum (enabled by default):
ALTER TABLE my_table SET (autovacuum_vacuum_scale_factor = 0.05);2. Schedule with cron or a separate job:
0 2 * * * /usr/bin/psql -U postgres -d mydb -c "VACUUM FULL ANALYZE;" 2>&1 | logger3. Use a maintenance window with a dedicated connection pool that has autocommit enabled.
Advanced notes
VACUUM is intentionally non-transactional because it requires exclusive locks and must persist changes to the physical database files immediately. PostgreSQL's autovacuum daemon handles routine maintenance automatically, so VACUUM FULL is rarely needed in production. If autovacuum is running too frequently, tune its parameters (autovacuum_naptime, autovacuum_vacuum_scale_factor, autovacuum_vacuum_cost_delay) rather than running manual VACUUM. For tables with GIN indexes, VACUUM also completes pending index insertions. When using connection pooling (PgBouncer, pgpool), ensure your VACUUM connections use session mode, not transaction mode.
Frequently asked questions
- What does "VACUUM cannot run inside a transaction block" mean?
- PostgreSQL requires VACUUM to run in autocommit mode because it performs maintenance operations that cannot be rolled back. The VACUUM command removes dead rows and optimizes table storage. When your database connection has an active transaction (the default in most ORMs and drivers), PostgreSQL blocks VACUUM to prevent inconsistent states.
- How do I fix "VACUUM cannot run inside a transaction block"?
- VACUUM is a PostgreSQL maintenance command that cannot run inside a transaction block. This error occurs when your connection has an active transaction. Enable autocommit mode or commit before running VACUUM.
- What causes "VACUUM cannot run inside a transaction block"?
- Your database connection has an active transaction (autocommit is disabled) Using an ORM or connection pool that defaults to autocommit=false Running VACUUM as part of a multi-statement query block
- Is the "VACUUM cannot run inside a transaction block" error serious?
- It is a medium-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)
- 📖PostgreSQL Error Codes(Official Docs)