How to fix Idle in transaction session timeout in PostgreSQL
PostgreSQL terminates idle transactions to prevent locks and table bloat. Configure idle_in_transaction_session_timeout to set a maximum idle period, or disable it (0ms) for maintenance operations.
Quick fix
Fixes Idle in transaction session timeout
SHOW idle_in_transaction_session_timeout;- Check the current timeout setting
- Adjust the timeout for your application
- Fix application code to avoid idle transactions
- Disable timeout for specific maintenance operations
SHOW idle_in_transaction_session_timeout;Idle in transaction session timeout
What this error means
This error occurs when a PostgreSQL session remains idle within an open transaction for longer than the configured idle_in_transaction_session_timeout value. PostgreSQL automatically terminates such sessions to reclaim resources. Idle transactions can hold locks that block other queries and prevent VACUUM from cleaning up dead rows, causing table bloat and index growth. The error message is: "ERROR: terminating connection due to idle-in-transaction timeout".
Typical symptoms
- Database connections unexpectedly drop mid-application
- Transactions fail with "terminating connection due to idle-in-transaction timeout" error
- Long-running application processes hang when waiting for user input within a transaction
- Batch operations fail after idle periods
- CI/CD pipelines fail during pg_dump or long-running maintenance
Common causes
- idle_in_transaction_session_timeout is set too low for your application workload
- Application opens transactions but waits for external events (user input, API calls) without committing
- Maintenance operations like pg_dump or index rebuilds have active transactions that become idle
- Connection pools not closing idle connections properly
- Long-running queries followed by idle periods within the same transaction
How to fix "Idle in transaction session timeout"
Connect to PostgreSQL and query the current value:
SHOW idle_in_transaction_session_timeout;A value of 0 means the timeout is disabled. Values are specified in milliseconds (e.g., 5000 = 5 seconds, 300000 = 5 minutes).
Set a timeout value appropriate for your application. Set it at the database, role, or session level:
Session level (temporary, current session only):
SET idle_in_transaction_session_timeout = '10min';Role level (persistent for specific users):
ALTER ROLE your_app_user SET idle_in_transaction_session_timeout = '10min';Database level (persistent for all users of this database):
ALTER DATABASE your_database SET idle_in_transaction_session_timeout = '5min';System-wide (in postgresql.conf):
idle_in_transaction_session_timeout = '5min'Then reload configuration:
SELECT pg_reload_conf();The best practice is to prevent idle transactions in the first place:
Commit transactions promptly:
// BAD: Opens transaction, then waits
const result = await db.transaction(async (trx) => {
await trx.insert({...});
// Application waits for user input here - transaction is idle
const userInput = await waitForUserInput();
return trx.update({...});
});
// GOOD: Minimize transaction scope
const userInput = await waitForUserInput();
const result = await db.transaction(async (trx) => {
await trx.insert({...});
return trx.update({...});
});Use connection pooling properly:
// Ensure connections are returned promptly
await client.query('BEGIN');
try {
await doWork();
await client.query('COMMIT');
} catch (e) {
await client.query('ROLLBACK');
} finally {
client.release(); // Important: release connection
}For long-running operations like pg_dump, backups, or index rebuilds, temporarily disable the timeout:
# Increase timeout for pg_dump
pg_dump --command="SET idle_in_transaction_session_timeout = 0" your_database > backup.sqlOr within a transaction:
SET idle_in_transaction_session_timeout = 0; -- Disable
CREATE INDEX CONCURRENTLY idx_large ON huge_table(column);
RESET idle_in_transaction_session_timeout; -- Re-enableFind long-running idle transactions:
SELECT pid, usename, application_name, state, state_change
FROM pg_stat_activity
WHERE state = 'idle in transaction'
ORDER BY state_change ASC;Check which queries are holding locks:
SELECT
l.locktype, l.database, l.relation, l.page, l.tuple,
a.usename, a.application_name, a.pid
FROM pg_locks l
JOIN pg_stat_activity a ON l.pid = a.pid
WHERE NOT l.granted;Advanced notes
The idle_in_transaction_session_timeout was introduced in PostgreSQL 9.6. Related timeout parameters: idle_session_timeout (applies to all idle sessions, not just those in transactions) and statement_timeout (limits execution time of individual queries, not idle time). Long-running statements are NOT affected by this timeout—only idle periods within transactions. When combined with connection pooling (PgBouncer, Pgpool-II), ensure pooler timeouts are configured separately and don't conflict with server timeouts. Some hosting providers (AWS RDS, Heroku) may enable this by default; check your provider's documentation.
Frequently asked questions
- What does "Idle in transaction session timeout" mean?
- This error occurs when a PostgreSQL session remains idle within an open transaction for longer than the configured idle_in_transaction_session_timeout value. PostgreSQL automatically terminates such sessions to reclaim resources. Idle transactions can hold locks that block other queries and prevent VACUUM from cleaning up dead rows, causing table bloat and index growth.
- How do I fix "Idle in transaction session timeout"?
- PostgreSQL terminates idle transactions to prevent locks and table bloat. Configure idle_in_transaction_session_timeout to set a maximum idle period, or disable it (0ms) for maintenance operations.
- What causes "Idle in transaction session timeout"?
- idle_in_transaction_session_timeout is set too low for your application workload Application opens transactions but waits for external events (user input, API calls) without committing Maintenance operations like pg_dump or index rebuilds have active transactions that become idle
- Is the "Idle in transaction session timeout" error serious?
- It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.