25P04 — transaction_timeout
25P04 — transaction_timeout
At a glance
25P04 is a FATAL condition emitted by transaction_timeout, which covers both an explicit BEGIN transaction and an implicitly started single-statement transaction. The selected 18.6 case uses 300 ms; PG10 is a source/version preflight skip because the setting is unavailable there, so it does not probe an unknown setting. A separate PG16.15/PG17.11 boundary comparison records unavailable-setting 42704 on PG16 and natural FATAL 25P04 on PG17; it is separate from the selected base case.
| Field | Value |
|---|---|
| SQLSTATE | 25P04 |
| Condition | transaction_timeout |
| Status | active |
| Known present by | 17.0 |
| Locked snapshots | 17.11, 18.6, 19beta3 |
| Macros | ERRCODE_TRANSACTION_TIMEOUT |
| Aliases | — |
Meaning
transaction_timeout limits the lifetime of an explicit or implicit transaction, including time spent executing or waiting while that transaction is open. When it fires, PostgreSQL emits terminating connection due to transaction timeout and terminates the session. It is different from statement_timeout, which aborts one statement, and from idle_in_transaction_session_timeout, which covers only client-idle time inside an open transaction. If transaction_timeout is shorter than or equal to either of those settings, the longer timeout is ignored; prepared transactions are not subject to it.
Diagnosis
On a supported server, capture the effective SHOW transaction_timeout, SHOW statement_timeout, and SHOW idle_in_transaction_session_timeout values on the timed session itself. A control connection’s SHOW or pg_settings describes the observer backend and cannot establish another session’s effective SET values. From the control connection inspect pg_stat_activity fields such as pid, application_name, state, xact_start, state_change, query_start, and query to identify the owning application or pool and whether the transaction is active or idle. Match the collector record by backend PID, SQLSTATE 25P04, error_severity = FATAL, and the exact message. In the selected 18.6 run the driver also exposed 25P04 and the original connection was closed; a fresh owner connection successfully ran SELECT 1. PG10 is skipped by the minimum-version preflight before SET transaction_timeout is sent.
Response
Discard the terminated session and reconnect. Keep legitimate transactions within the chosen budget by shortening work, splitting a long unit of work, or raising the effective timeout; do not lower it to hide the error. Set it to 0 only when the deployment deliberately accepts the absence of this guard. When the server closes the terminated session, it rolls back the open incomplete transaction before exiting; the dead connection cannot accept a later ROLLBACK. A known transaction that had not committed on that session cannot be recovered through that connection. If a separate network failure left it uncertain whether COMMIT reached the server, reconcile the business result from a fresh connection before retrying; the selected case sent no commit and does not establish that uncertainty. PG10 has no compatible setting in this batch.
FATAL message
The server source emits terminating connection due to transaction timeout at FATAL severity; the original connection is terminated.
Observed diagnostics
18.6 (Homebrew) / latest: FATAL SQLSTATE 25P04; primary terminating connection due to transaction timeout; backend PID 30576; original connection closed True; collector CSV 25P04; collector JSON 25P04; fresh probe 1.
10.21 (Debian 10.21-1.pgdg90+1) / pg10: case not_applicable (minimum-version preflight; the unsupported setting was not probed).
Separate boundary comparison: 16.15 / pg16 returned 42704 for unavailable transaction_timeout and remained IDLE, so it is not 25P04 coverage. 17.11 / pg17 produced FATAL 25P04 after 300 ms with backend PID 73; CSV and JSON both recorded FATAL 25P04, the original connection closed, and a fresh connection was IDLE. These records are outside the selected base case.
Representative case
The SQL excerpt below is not a paste-all script: run the setting, PID, and BEGIN on a test connection, then stop sending it queries while a separate observer or log collector waits for FATAL; after the old connection is terminated, open a fresh owner connection for the final probe. The runner reads these statements from the shared registry; complete assertions, collector correlation, environment, and cleanup are in case export.
The SQLSTATE, diagnostics, states, and repair assertions for this excerpt come from the shared registry (SHA-256 d05a26e56716c5c8c178f741843e6b14be87423ec9d1eb1a026ff67a57ce4658); structured evidence.
Authored evidence IDs: identity, timeout-path, runtime, runtime.version-boundary. Selected base runtime record: runtime.25P04-batch2c-latest-20260909.latest. Separate boundary records: runtime.25P04-boundary-pg16-20260909.pg16 and runtime.25P04-boundary-pg17-20260909.pg17.
The separate boundary comparison has already recorded the availability check: PG16 returned 42704 and PG17 continued to a natural FATAL 25P04. This boundary block is branched, not a paste-all script. On PG16, run SHOW transaction_timeout; after the expected 42704, use the same still-healthy connection for SELECT 1 if you need to prove recovery, then stop and skip SET transaction_timeout, BEGIN, and the timeout wait. On PG17 or later, after SHOW succeeds, run SET transaction_timeout, capture the PID, and run BEGIN; then stop sending queries and let the observer or collector wait for the old connection’s FATAL and closure. Open a fresh connection only for the final probe. The boundary records are diagnostic comparisons and do not add to the selected base case count.
Versions and limits
The locked catalogue first observes transaction_timeout in 17.0. The selected 18.6 case passes with CSV/JSON collector evidence and a fresh-connection probe. The separate PG17.11 boundary case independently records natural FATAL 25P04, while PG16.15 records unavailable-setting 42704; neither replaces the selected base run. The PG10 case is a real not_applicable minimum-version preflight skip: the handler does not send or probe the unknown setting, so this is not evidence of an earlier boundary.
Related
25P03 idle-in-transaction timeout, 40001 serialization failure, 57014 query canceled.
Sources
src.errcodes.18.6(SHA-2566e8de346643ba84aa3c9c6a73360acfc7b2dfb89162c06c08ce9bf5bcd5bbcba)src.calls.REL_18_6—raw/calls/REL_18_6.jsonl(SHA-2569ee8a0e81d8f0825c5c1ae45583439859a26e602bdd4ce2f2a62aa278867ccbf)src.postgres.18.6(SHA-2569fb62275b1badf94d01ab351337b60410cd9b3ab1fe63fa9f23d6d2185a21061)src.config.18.6(SHA-256ffca322c15315ba670733d6b320f23cbba7374db0d18937298ffc07b58799ef6) · official documentationdoc.monitoring.18.6(SHA-25606b147f8d966fe46821871703ffcee8a3449cc3f7de5b64b9177f48569fe96f6) · official monitoring documentationsrc.system_views.18.6(SHA-256418f4ae7472fe0742e6fbdc7a055581602c5131e082aa6f17e2c7269bf711972) · officialpg_settingsdocumentationdoc.protocol.18.6(SHA-256745339e07ebbc2bb67d98b258e25772ada644ef26011e3cd6b3fcbb435235f89) · official protocol documentation