Incident brief
Idle in transaction session timeout
A session left an open transaction sitting idle (no query in flight) for longer than idle_in_transaction_session_timeout. PostgreSQL terminated the connection instead of letting it hold locks and a snapshot indefinitely.
In 10 seconds
- What
- Idle in transaction session timeout
- What triggers it
- SET idle_in_transaction_session_timeout to a short value.
- The fix
- Don't leave a BEGIN open while waiting on something outside the database (user input, an external API call). Commit or roll back first, then reopen a transaction when you're ready to continue.
- Proof
- Reproduced on PostgreSQL 16.14 → A transaction left genuinely idle (no query sent, only a client-side pause) past the configured timeout was terminated by the server with SQLSTATE 25P03.
The fix
What to do right now
The immediate, application-level response to this error.
- Don't leave a BEGIN open while waiting on something outside the database (user input, an external API call). Commit or roll back first, then reopen a transaction when you're ready to continue.
- Set idle_in_transaction_session_timeout as a safety net so a forgotten open transaction can't hold locks and an old snapshot forever.
-- application pattern: don't hold BEGIN open across an external wait
COMMIT; -- release the transaction before waiting on something external
-- ... do the external work here, outside any transaction ...
BEGIN; -- reopen only when ready to continueDiagnose
See it live on the server
Run these against the affected instance to confirm the diagnosis before you act.
Standard triage — not specific to this error
These are canonical PostgreSQL system-catalog queries, shown as SQL to run. No sample output is attached because this is general triage, not a captured lab transcript.This SQLSTATE does not have an error-specific live snapshot yet. These are the canonical system-catalog queries you run against the affected server to see the problem in real time — standard triage, not a reproduced transcript.
What is running right now
Active backends, how long each has been running, and what it is waiting on.
SELECT pid,
state,
wait_event_type,
wait_event,
now() - query_start AS running_for,
left(query, 80) AS query
FROM pg_stat_activity
WHERE state <> 'idle'
AND pid <> pg_backend_pid()
ORDER BY running_for DESC NULLS LAST;Who is blocking whom
Turn raw blocking PIDs into the actual queries on both sides of the wait.
SELECT blocked.pid AS blocked_pid,
blocked.query AS blocked_query,
blocking.pid AS blocking_pid,
blocking.query AS blocking_query
FROM pg_stat_activity AS blocked
JOIN LATERAL unnest(pg_blocking_pids(blocked.pid)) AS b(pid) ON true
JOIN pg_stat_activity AS blocking ON blocking.pid = b.pid
WHERE cardinality(pg_blocking_pids(blocked.pid)) > 0;Locks that are still waiting
Every lock a backend has requested but not yet been granted.
SELECT l.pid,
l.locktype,
l.mode,
l.granted,
COALESCE(c.relname, l.transactionid::text) AS object
FROM pg_locks l
LEFT JOIN pg_class c ON c.oid = l.relation
WHERE NOT l.granted
ORDER BY l.pid;Why it happens
What PostgreSQL is telling you
The mechanism behind the error, grounded in the official manual — not paraphrased.
PostgreSQL 16 Documentation — Client Connection Defaults (20.11)
Terminate any session with an open transaction that has been idle for longer than the specified duration in milliseconds.Read the full section on postgresql.org →
The idle transaction
The transaction opened and ran one statement normally — the clock only starts once the session goes idle with no query in flight.What the next statement sees
By the time the next statement was sent, the session had been idle-in-transaction past the 1-second limit, so PostgreSQL had already terminated the connection.Reproduce & verify
A real, single-session PostgreSQL reproduction
A literal transcript of SQL run against a live PostgreSQL instance in an isolated lab — the commands below are exactly what was executed.
- 1SET idle_in_transaction_session_timeout to a short value.
- 2BEGIN a transaction, run one statement, then go idle (send nothing) past the timeout.
- 3The connection is terminated with SQLSTATE 25P03 the moment the client sends its next statement.
One client: lower the timeout, open a transaction, run one statement, then pause client-side (sending nothing) past the timeout.
SET idle_in_transaction_session_timeout = '1s';BEGIN;
SELECT 1;
-- client goes idle here for 3s, sending nothing to the serverSELECT 2; -- sent after the idle pauseWhat PostgreSQL actually returned
SETBEGIN
?column?
----------
1
(1 row)FATAL: terminating connection due to idle-in-transaction timeout
server closed the connection unexpectedly
This probably means the server terminated abnormally
before or while processing the request.
connection to server was lostThe deeper lab audit for this error
What Pro unlocks here
- The exact prevention SQL — copy-paste ready
- Raw psql output captured from the Docker lab
- A senior-DBA action list to take it further
- Live monitoring queries to catch it in production
- The deeper audit: fix-that-fails counterexample, GUC before/after, server-log evidence
Related & next steps
Follow the thread
Everything this error touches — jump straight to the sibling error, term, runbook, or parameter.
Verification
- Last verified
- 2026-07-16 (Docker lab, PostgreSQL 16.14)
- Reviewed by
- Verified against PostgreSQL 16.14 in an isolated lab environment
- Audit status
- reviewed