P0004 — assert_failure
P0004
At a glance
P0004 is PL/pgSQL assert_failure. The fixed ASSERT path reports ERROR with either the evaluated message or assertion failed.
| Field | Value |
|---|---|
| SQLSTATE | P0004 |
| Condition | assert_failure |
| Status | active |
| Known present by | 9.5.0 |
| Locked snapshots | 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_ASSERT_FAILURE |
| Aliases | — |
The shared assertion_failure_recovery case passed on PostgreSQL 18.6 and PostgreSQL 10.21. With plpgsql.check_asserts enabled, the false ASSERT returned P0004 with value must be positive and left the explicit transaction INERROR; ROLLBACK restored IDLE, a valid input succeeded, and the disabled-assert control returned zero.
Meaning
An ASSERT is an executable invariant check inside PL/pgSQL. PostgreSQL evaluates its Boolean condition; a false or NULL result takes the assertion-failure path. The fixed executor then reports ERROR with SQLSTATE P0004: it evaluates the message expression only on that path, uses a non-NULL result as the primary text, and uses assertion failed for a NULL or omitted message. With checks disabled, it skips both the condition and message expression.
plpgsql.check_asserts is a session setting that controls whether ASSERT statements are checked. With it enabled, a failed assertion is a real ERROR and an explicit transaction enters INERROR. With it disabled, ASSERT statements are skipped; the same false input does not prove the invariant and should not be used as a production validation substitute.
The selected runtime exercises a false condition with a non-NULL message and a false condition with checks disabled. The NULL condition and the no-message fallback are source-confirmed semantics, not additional natural observations in this batch.
Diagnosis
Start with the ErrorResponse fields and the server log entry together. In the fixed 18.6 path, message_primary is either the evaluated message or assertion failed; the runtime case also recorded ERROR, P0004, exec_stmt_assert, and context identifying the PL/pgSQL function and line 1 at ASSERT. Check SHOW plpgsql.check_asserts on the affected session, because a setting change in another connection does not change this one.
If the primary is assertion failed, check whether the ASSERT had no message or its message expression evaluated to NULL. If there is no P0004 at all, inspect SHOW plpgsql.check_asserts first: a disabled setting skips the check before evaluating its condition. Keep the function source and the session setting together when comparing two calls; a pooled connection can have a different setting from the one that created the function.
Separate a broken program invariant from expected business input. An assertion such as value > 0 is useful for an assumption that should always hold after validation; an expected negative user value belongs in ordinary validation, a constraint, or an explicit application error with a deliberate SQLSTATE. If the error occurred inside an explicit transaction, inspect the transaction state before issuing another command: the observed case was INERROR until ROLLBACK, not a connection failure.
Response
For an explicit transaction, issue ROLLBACK before unrelated work, then reproduce with the corrected invariant or input. The shared case verified ROLLBACK → IDLE, a valid call returning 1, and the same assertion remaining silent when plpgsql.check_asserts was set to off.
Use WHEN ASSERT_FAILURE when a PL/pgSQL block deliberately wants to handle this named condition. WHEN OTHERS does not catch ASSERT_FAILURE, so a broad handler cannot be used as a hidden assertion switch. An exception block can recover through its documented subtransaction boundary, but swallowing the failure without checking the invariant leaves the program assumption unverified. Keep assertions enabled while diagnosing; disabling them is a diagnostic control, not a repair.
Versions
The locked catalogue records P0004 from 9.5.0 through the listed formal snapshots and 19beta3. The fixed executor source is PostgreSQL 18.6 pl_exec.c#L3965-L3968. The official PL/pgSQL error and message documentation covers ASSERT and named conditions; the control-structures error-trapping documentation defines the EXCEPTION subtransaction and handler matching boundary. The latest/PG10 runtime case confirms the described P0004 and transaction behavior for the shared function.
Related
P0002 for PL/pgSQL no-data handling, P0003 for strict multi-row handling, and P0000 for the PL/pgSQL error category. Use the actual SQLSTATE from the response rather than treating every PL/pgSQL failure as P0004.
Sources
The fixed implementation is pl_exec.c#L3965-L3968. The official PL/pgSQL error and message reference documents ASSERT semantics, while the control-structures error-trapping reference documents the WHEN OTHERS exclusion and subtransaction boundary. The structured evidence record pins the source SHA, runtime summaries, raw results, and shared snippet registry.