select open change scope Open full search

PG.CENTER connects PostgreSQL documentation, reference, and ecosystem knowledge. Maintained by Pigsty.

SQLSTATE / CLASS 25 · INVALID TRANSACTION STATE

transaction_timeout

SQLSTATE
25P04
Condition name
transaction_timeout
Class
Invalid Transaction State
Source macro
ERRCODE_TRANSACTION_TIMEOUT
Evidence
Observed at runtime in the source evidence
Analysis & operational context

English SQLSTATE atlas: authored explanations and source/runtime evidence are separate from the manual definitions. View source ↗

<h1>25P04 — transaction_timeout</h1>

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.

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.

-- set_timeout
SET transaction_timeout = '300ms';
-- backend_pid
SELECT pg_backend_pid();
-- begin
BEGIN;
-- probe
SELECT 1;

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.

-- availability_probe
SHOW transaction_timeout;
-- set_timeout
SET transaction_timeout = '300ms';
-- backend_pid
SELECT pg_backend_pid();
-- begin
BEGIN;
-- probe
SELECT 1;

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.

Sources

Source evidence

Evidence belongs to the frozen source and runtime versions listed here. It is not a runtime verification of the selected manual version.

25P04 is transaction_timeout in Class 25.

Method: Read the fixed condition row and macro.

Limits: The locked catalogue first observes this condition in PostgreSQL 17.0; no earlier runtime claim is made.

src.errcodes.18.6

ProcessInterrupts emits FATAL 25P04 when an explicit or implicitly started transaction exceeds transaction_timeout.

Method: Trace the timeout interrupt and the fixed configuration documentation.

Limits: The selected PG10 target lacks this setting and is explicitly not applicable; the session is terminated when the path fires.

src.postgres.18.6 src.config.18.6 src.calls.REL_18_6

The selected PostgreSQL 18.6 case received FATAL 25P04 for the exact backend PID and recovered only by opening a fresh connection; the PG10 case is not applicable.

Method: Match collector records by PID, SQLSTATE, and message, then probe a fresh connection.

Limits: This verifies 18.6 and the PG10 applicability boundary only; it does not claim all intermediate releases without an adjacent source check.

runtime runtime.25P04-batch2c-latest-20260909.latest runtime.25P04-batch2c-pg10-20260909.pg10

The fixed monitoring documentation defines pg_stat_activity as one row per server process with current activity, including xact_start and state_change; pg_settings is defined over pg_show_all_settings for the current backend. Capture timeout settings on the timed session itself, because SHOW or pg_settings on an observer connection cannot establish another backend's effective SET value.

Method: Read the fixed monitoring view documentation and system-view definition before writing the observer/session diagnostic split.

Limits: Visibility of pg_stat_activity fields depends on observer privileges; the selected runtime records establish PID/log correlation, not universal monitoring permissions.

doc.monitoring.18.6 src.system_views.18.6

The fixed protocol documentation states that when the backend closes a connection it rolls back any open incomplete transaction before exiting; the driver cannot send a later ROLLBACK on that closed session.

Method: Read the fixed protocol session-termination rule and align the recovery prose with the FATAL path.

Limits: This establishes server-side rollback on forced connection close; it does not resolve a separate network failure after a client may have sent COMMIT.

doc.protocol.18.6

On the fixed availability-boundary registry case, PostgreSQL 16.15 rejects SHOW transaction_timeout with 42704 and remains IDLE, while PostgreSQL 17.11 receives FATAL 25P04 after a 300 ms timeout in an idle-in-transaction session; the original session closes and a fresh connection is IDLE.

Method: Compare the fixed PG16 availability probe and PG17 timeout summaries, including driver diagnostics, both collector severities, PID correlation, termination, and fresh-connection recovery.

Limits: This is a bounded PG16.15/PG17.11 comparison for transaction_timeout. PG16 42704 is not 25P04 coverage; the records do not replace the selected 18.6 run or establish every intermediate minor release.

src.config.18.6 doc.protocol.18.6 runtime.25P04-boundary-pg16-20260909.pg16 runtime.25P04-boundary-pg17-20260909.pg17

Message templates

FATAL · fatal

Primary

terminating connection due to transaction timeout

This is a static FATAL message; the driver may expose or omit SQLSTATE depending on client stack.

Reproduction & repair cases

transaction_timeout · PG 18

Preconditions

  • A runner-owned disposable target is provisioned.

Trigger: On a server supporting transaction_timeout, set it, BEGIN, wait, and probe the terminated session.

Expected assertions

  • The original session is terminated by the timeout when supported
  • The collector records FATAL 25P04 for the matching backend PID
  • A fresh connection can execute SELECT 1
  • PG10 is explicitly not applicable

Repair: Use the state-specific recovery documented on the page; do not reuse a terminated connection.

Cleanup: Drop the case schema with an owner connection.

transaction_timeout_version_boundary · PG 16, 17

Preconditions

  • A disposable PostgreSQL 16 or 17 target

Trigger: Probe transaction_timeout on PG16; on PG17 set it, BEGIN, wait, and probe the terminated session.

Expected assertions

  • PG16 records the actual unsupported-setting diagnostic and recovers
  • PG17 records exact FATAL 25P04 collector fields and a registry-based fresh connection probe

Repair: Treat PG16 as unsupported for this setting; for PG17 use a fresh connection after the FATAL and determine unfinished work from the application transaction boundary.

Cleanup: Drop the case schema with an owner connection.

Recorded runtime evidence

18.6 (Homebrew) · passed

Run: 25P04-batch2c-latest-20260909

{
  "primary": "terminating connection due to transaction timeout",
  "severity": "FATAL",
  "sqlstate": "25P04",
  "backend_pid": 30576,
  "status_before_timeout": "INTRANS",
  "collector_csv_sqlstate": "25P04",
  "fresh_connection_probe": 1,
  "collector_json_sqlstate": "25P04",
  "fresh_connection_status": "IDLE",
  "original_connection_closed": true,
  "collector_csv_error_severity": "FATAL",
  "collector_json_error_severity": "FATAL",
  "recovery_requires_new_connection": true
}
10.21 (Debian 10.21-1.pgdg90+1) · not_applicable

Run: 25P04-batch2c-pg10-20260909

{
  "setting": "transaction_timeout",
  "preflight": "minimum_version",
  "case_status": "not_applicable",
  "minimum_version": 170000,
  "server_version_num": 100021,
  "setting_probe_skipped": true
}
16.15 (Debian 16.15-1.pgdg13+2) · not_applicable

Run: 25P04-boundary-pg16-20260909

The case passed its availability assertions, but this target does not have the setting. The 42704 probe is retained as a version comparison and is not counted as a 25P04 runtime.

{
  "setting": "transaction_timeout",
  "status_after_probe": "IDLE",
  "availability_primary": "unrecognized configuration parameter \"transaction_timeout\"",
  "availability_severity": "ERROR",
  "availability_sqlstate": "42704",
  "fresh_connection_probe": 1,
  "natural_25P04_observed": false
}
17.11 (Debian 17.11-1.pgdg13+2) · passed

Run: 25P04-boundary-pg17-20260909

This separate PG17.11 boundary case demonstrates the natural FATAL path and fresh-session recovery; it does not replace or inflate the selected base 18.6 case.

{
  "primary": "terminating connection due to transaction timeout",
  "setting": "transaction_timeout",
  "sqlstate": "25P04",
  "timeout_ms": 300,
  "backend_pid": 73,
  "driver_severity": "FATAL",
  "status_before_timeout": "INTRANS",
  "collector_csv_severity": "FATAL",
  "collector_csv_sqlstate": "25P04",
  "fresh_connection_probe": 1,
  "natural_25P04_observed": true,
  "collector_json_severity": "FATAL",
  "collector_json_sqlstate": "25P04",
  "fresh_connection_status": "IDLE",
  "original_connection_closed": true
}
Version history 1
  1. PG 16 → 17added

Definition snapshot: english-manuals:2c0bc2b532a9269703f2ee7a783… · English manual source