40001 — serialization_failure
At a glance
40001 is PostgreSQL’s serialization_failure condition in Class 40, transaction_rollback. It tells the client that the transaction’s observed ordering cannot be serialized with a concurrent transaction. PostgreSQL aborts the conflicting transaction so the client can retry.
The representative case starts two SERIALIZABLE transactions that read the same value. The first updates and commits. The stale transaction then receives could not serialize access due to concurrent update, enters INERROR, and returns to IDLE only after ROLLBACK. A new serializable transaction reads the committed value, applies the registry’s fixed SET value = 2 operation, commits, and produces the final result; this is a transaction-boundary proof, not a test of an application business calculation.
Run 40001-manual-boundary-final-20260909 passed on PostgreSQL 18.6 and isolated PostgreSQL 10.21. The per-target assertions and structured observations are retained in the public evidence JSON.
| Field | Value |
|---|---|
| SQLSTATE | 40001 |
| Condition | serialization_failure |
| Status | active |
| Known present by | 7.4 |
| Locked snapshots | 9.0.23, 9.1.24, 9.2.24, 9.3.25, 9.4.26, 9.5.25, 9.6.24, 10.23, 11.22, 12.22, 13.23, 14.24, 15.19, 16.15, 17.11, 18.6, 19beta3 |
| Macros | ERRCODE_T_R_SERIALIZATION_FAILURE |
| Aliases | — |
Meaning and trigger paths
At SERIALIZABLE isolation PostgreSQL uses predicate and tuple-conflict tracking to reject a transaction when its result would depend on an order that cannot be made serial. A concurrent update of a row read by a stale transaction is one concrete path. The SQLSTATE describes the transaction outcome; it does not identify the business operation that should be retried.
The executor’s update path reports ERRCODE_T_R_SERIALIZATION_FAILURE with the source message could not serialize access due to concurrent update. Other serialization conflicts can use a different message or a recovery-conflict detail while retaining the class’s transaction-rollback meaning. Preserve the complete diagnostic and the transaction’s read/write set in application logs.
40001 differs from 23505: a duplicate requested by the user is not automatically a serialization failure, even though some concurrent key-selection designs can surface a unique violation that the application treats as retryable. Decide the retry policy from the operation’s semantics and the complete transaction history.
Messages and diagnostics
The case schedule below must use two sessions for the two initial snapshots. The final block is a new connection and a new serializable snapshot; it is the required complete retry.
PostgreSQL 18.6 returned:
PostgreSQL 10.21 produced the same primary message with its version-specific nodeModifyTable.c line. message_detail is absent in this path; other conflict origins may attach additional fields. The SQLSTATE and the aborted transaction state are the stable retry signals.
Diagnosis
Capture SQLSTATE, severity, primary message, detail, hint, context, isolation level, the statements that formed the snapshot, and transaction status. Determine which transaction committed first and which reads became stale. The runner asserted both initial reads were 0, the first commit returned IDLE, and the stale transaction was INERROR before rollback.
Do not issue a follow-up query on the failed transaction. Roll it back, start a new transaction, and repeat the complete read/decision/write sequence. Replaying only the last UPDATE can apply a decision based on a snapshot that is no longer valid.
Response and repair
Treat 40001 as a transaction retry signal when the operation is designed for it:
- Roll back the entire failed transaction and release its locks.
- Begin a new transaction at the required isolation level and re-read all values used by the business decision.
- Apply the operation again, with a bounded backoff and a maximum retry count.
- Make the operation idempotent and verify the final business result after commit.
The representative retry read 1, wrote 2, committed with status IDLE, and a separate read observed final value 2. A fixed assignment in this disposable case does not prove that an arbitrary production calculation is safe to replay; the application must recompute from the fresh snapshot.
Versions and boundaries
The catalogue has a definition-presence observation for 40001 at PostgreSQL 7.4 and through the locked 8.4.22 pre-9.0 definitions, then in every listed formal snapshot through PostgreSQL 18.6 and the PostgreSQL 19 Beta 3 preview. This is a definition-only presence boundary, not an exact implementation introduction or runtime-use claim. The class title observation between 9.0 and 9.1 is recorded in the catalogue; the condition has no other recorded definition change in the scanned range.
The stale-update case passed on PostgreSQL 18.6 and 10.21. Message source lines and conflict details vary by release and conflict type. This evidence covers a stale serializable update and its fresh complete retry; it does not claim that every 40001 source path has the same message or that every transaction can be retried safely.
Related
40P01 — deadlock_detected also aborts a transaction and can require a complete retry, but its trigger is a lock cycle. 23505 — unique_violation is a distinct integrity condition and is not automatically retryable. 23503 — foreign_key_violation can be a permanent data relationship error. 25P02 — in_failed_sql_transaction is the follow-on state before a failed transaction is rolled back.
Sources
Structured evidence is recorded in the public evidence JSON. Source records use PostgreSQL commit 724edf9bde9d356724ad384a2e196edc3c9f80f7; run records retain exact target IDs and structured observations.
src.errcodes.18.6—errcodes.txtsrc.nodeModifyTable.18.6—nodeModifyTable.cdoc.mvcc.18— Serialization failure handling- Runtime:
40001-manual-boundary-final-20260909on latest and pg10; structured observations are in the public evidence JSON