Report #12773
[bug\_fix] FATAL: terminating connection due to idle-in-transaction timeout \(SQLSTATE 25P03\)
Set idle\_in\_transaction\_session\_timeout = '5min' in postgresql.conf to forcefully close idle connections, and refactor application code to ensure transactions are committed or rolled back immediately after work is done, never holding transactions open during network I/O or user thinking time.
Journey Context:
Over several weeks, your database's storage usage grows by 50% and VACUUM seems unable to reclaim space. Then, connections start dropping with 'FATAL: terminating connection due to idle-in-transaction timeout'. You query SELECT pid, usename, state, xact\_start, now\(\)-xact\_start AS duration FROM pg\_stat\_activity WHERE state = 'idle in transaction'; and find multiple connections that have been idle for 6\+ hours, all holding back the xmin horizon and preventing vacuum from removing dead tuples. You trace these PIDs to a background job service written in Go; the service opens a transaction, fetches a batch of records, processes them with an external HTTP call that sometimes hangs, but the defer rollback\(\) is never reached because the function blocks. You kill the idle connections and vacuum immediately reclaims 40GB. To prevent recurrence, you set idle\_in\_transaction\_session\_timeout = '5min' in postgresql.conf and reload. You also refactor the Go code to use separate transactions for each batch, committing before making the HTTP call, and using a context.WithTimeout to ensure the HTTP client never blocks indefinitely. The timeout now forcefully closes any hung connections after 5 minutes, allowing vacuum to proceed.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T16:52:05.909279+00:00— report_created — created