Incident brief
PL/pgSQL assertion failure
A PL/pgSQL ASSERT whose condition evaluates to false raises this error with the assertion's message; it signals a violated invariant, not ordinary data validation.
What lands in your log
ERROR: ledger must have exactly one open row
In 10 seconds
- What triggers it
- Run a PL/pgSQL block containing ASSERT <condition>, <message> where the condition is false at runtime.
- Fix
- Make the asserted invariant hold before the ASSERT (fix the data or the preceding logic).
- Proof
- Reproduced on PostgreSQL 18.4 → A DO block reproduces SQLSTATE P0004; a false ASSERT condition aborts with its custom message and names the ASSERT in the CONTEXT.
Fix
What to do right now
Application-level steps for this error.
- Make the asserted invariant hold before the ASSERT (fix the data or the preceding logic).
- Reserve ASSERT for should-never-happen invariants; use RAISE or validation for expected bad input.
-- Ensure the asserted invariant holds.
DO $$ BEGIN ASSERT (SELECT count(*) FROM (VALUES (1)) v) = 1, 'ok'; END $$;For this error
See this error live on the server
Run these against the affected instance to confirm the diagnosis before you act.
PL/pgSQL assertion failure comes from PL/pgSQL control flow. Inspect the exact stored function body and line reported in CONTEXT before changing server settings.
Stored PL/pgSQL source for the failing function
Replace the function signature from CONTEXT; this preserves overload identity.
SELECT pg_get_functiondef(to_regprocedure('<schema.function(argument_types)>'));PL/pgSQL assertion setting
Relevant to assertion failures; harmless context for the other PL/pgSQL control-flow errors.
SELECT current_setting('plpgsql.check_asserts', true) AS plpgsql_check_asserts;Why it happens
What PostgreSQL is telling you
The mechanism behind the error, grounded in the official manual, not paraphrased.
PostgreSQL 18 Documentation, Appendix A. PostgreSQL Error Codes (Table A.1, Class P0, PL/pgSQL Error)
P0004 → assert_failureRead the full section on postgresql.org →
Failing a PL/pgSQL ASSERT
The ASSERT condition was false, so PL/pgSQL raises the assertion message 'ledger must have exactly one open row' with SQLSTATE P0004.The session continues normally
The ASSERT failure happened on a standalone call, so nothing carries over into session B's next statement.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.
- 1Run a PL/pgSQL block containing ASSERT <condition>, <message> where the condition is false at runtime.
- 2PL/pgSQL evaluates the ASSERT and finds the invariant violated.
- 3The block aborts with SQLSTATE P0004 and the assertion message, pointing at the ASSERT in the CONTEXT.
A routine asserted that a ledger had exactly one open row, and a state where that was untrue tripped the assertion.
-- The error is self-contained in one statement; no schema is required.
SELECT 'no schema needed' AS setup_note;DO $$ BEGIN ASSERT (SELECT count(*) FROM (VALUES (1),(2)) v) = 1, 'ledger must have exactly one open row'; END $$;SELECT 'ok' AS session_after_error;What PostgreSQL actually returned
setup_note
------------------
no schema needed
(1 row)ERROR: ledger must have exactly one open row
CONTEXT: PL/pgSQL function inline_code_block line 1 at ASSERT session_after_error
---------------------
ok
(1 row)The block runs cleanly once the asserted invariant holds.
Without this
Before: a false ASSERT aborts
With this, tested
After: the invariant holds and the block runs
- A second operational test: exact SQL, raw output, measured result, and engineer notes
Card required. Cancel before day 7 and you are not charged.
Connected
Everything this error touches
Every page this SQLSTATE connects to: the concept that explains it, the runbooks that fix it, the parameters you tune to prevent it, and the sibling errors it travels with. All real cross-references. Jump straight in, or open the full interactive map.
Verification
- Last verified
- 2026-07-24 (isolated lab, PostgreSQL 18.4)
- Verification scope
- Verified against PostgreSQL 18.4 in an isolated lab environment
- Audit status
- reviewed
Went further?
Pro unlocks the second lab proof
Free page stops the bleeding. Pro adds the operational test, SQLSTATE audit, and deeper evidence, same error, more certainty.