Incident brief
Current transaction is aborted, commands ignored until end of transaction block
One statement inside a transaction failed. PostgreSQL now rejects every later command in that same transaction until it sees ROLLBACK, even if those later commands are perfectly valid on their own.
In 10 seconds
- What
- Current transaction is aborted, commands ignored until end of transaction block
- What triggers it
- Start a transaction with BEGIN.
- The fix
- ROLLBACK the transaction and start again.
- Proof
- Reproduced on PostgreSQL 16.14 → The division-by-zero error aborted the transaction. The next INSERT was rejected even though it is a valid statement on its own, and ROLLBACK discarded both inserts — the table ended up empty.
The fix
What to do right now
The immediate, application-level response to this error.
- ROLLBACK the transaction and start again.
- Or place a SAVEPOINT before risky statements so you can ROLLBACK TO the savepoint instead of losing everything.
- Stop sending commands as soon as the first error appears. They will all be rejected until ROLLBACK.
BEGIN;
INSERT INTO finance.ledger (id, amount) VALUES (10, 100);
SAVEPOINT before_risky;
-- a statement that might fail goes here
ROLLBACK TO SAVEPOINT before_risky;
-- the transaction is usable again
INSERT INTO finance.ledger (id, amount) VALUES (11, 50);
COMMIT;Diagnose
See it live on the server
Run these against the affected instance to confirm the diagnosis before you act.
Standard triage — not specific to this error
These are canonical PostgreSQL system-catalog queries, shown as SQL to run. No sample output is attached because this is general triage, not a captured lab transcript.This SQLSTATE does not have an error-specific live snapshot yet. These are the canonical system-catalog queries you run against the affected server to see the problem in real time — standard triage, not a reproduced transcript.
What is running right now
Active backends, how long each has been running, and what it is waiting on.
SELECT pid,
state,
wait_event_type,
wait_event,
now() - query_start AS running_for,
left(query, 80) AS query
FROM pg_stat_activity
WHERE state <> 'idle'
AND pid <> pg_backend_pid()
ORDER BY running_for DESC NULLS LAST;Who is blocking whom
Turn raw blocking PIDs into the actual queries on both sides of the wait.
SELECT blocked.pid AS blocked_pid,
blocked.query AS blocked_query,
blocking.pid AS blocking_pid,
blocking.query AS blocking_query
FROM pg_stat_activity AS blocked
JOIN LATERAL unnest(pg_blocking_pids(blocked.pid)) AS b(pid) ON true
JOIN pg_stat_activity AS blocking ON blocking.pid = b.pid
WHERE cardinality(pg_blocking_pids(blocked.pid)) > 0;Locks that are still waiting
Every lock a backend has requested but not yet been granted.
SELECT l.pid,
l.locktype,
l.mode,
l.granted,
COALESCE(c.relname, l.transactionid::text) AS object
FROM pg_locks l
LEFT JOIN pg_class c ON c.oid = l.relation
WHERE NOT l.granted
ORDER BY l.pid;Why it happens
What PostgreSQL is telling you
The mechanism behind the error, grounded in the official manual — not paraphrased.
PostgreSQL 16 Documentation — §3.4 Transactions (Savepoints)
ROLLBACK TO is the only way to regain control of a transaction block that was put in aborted state by the system due to an error, short of rolling it back completely and starting again.Read the full section on postgresql.org →
The transaction (fails partway through)
The division by zero is a real error, so PostgreSQL marks the whole transaction as aborted. The second INSERT is completely valid SQL, but SQLSTATE 25P02 says PostgreSQL will not even try to run it — the only way out of an aborted transaction is ROLLBACK (or ROLLBACK TO a savepoint).Checking the table afterward
The table is empty. ROLLBACK undid the first INSERT too, because both statements were inside the same transaction that never committed. Nothing was ever saved.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.
- 1Start a transaction with BEGIN.
- 2Run one statement that succeeds, for example an INSERT.
- 3Run a statement that fails, for example a division by zero.
- 4Try to run another normal statement in the same transaction.
- 5PostgreSQL rejects it with SQLSTATE 25P02, even though that statement is fine on its own.
One client, one transaction: a valid insert, then a failing statement, then another valid insert, then ROLLBACK.
DROP TABLE IF EXISTS finance.ledger;
CREATE TABLE finance.ledger (
id integer primary key,
amount integer not null
);BEGIN;
INSERT INTO finance.ledger (id, amount) VALUES (1, 100);
-- this line breaks: division by zero
SELECT 1/0;
-- the transaction is now aborted; this normal insert is rejected too
INSERT INTO finance.ledger (id, amount) VALUES (2, 50);
ROLLBACK;SELECT * FROM finance.ledger ORDER BY id;What PostgreSQL actually returned
DROP TABLE
CREATE TABLEBEGIN
INSERT 0 1
ERROR: division by zero
ERROR: current transaction is aborted, commands ignored until end of transaction block
ROLLBACK id | amount
----+--------
(0 rows)A SAVEPOINT placed before the risky statement lets a transaction recover from an error without losing everything already done. This was run against the same failure to prove it actually works.
Insert row 10, mark a savepoint, hit the same division-by-zero error, roll back only to the savepoint instead of the whole transaction, then insert row 11 and commit.
Without this
Above: without a savepoint, the error aborted the whole transaction and ROLLBACK discarded both inserts.
With this, tested
Below: with a savepoint before the risky statement, only the risky part was undone — the transaction recovered and committed.
What Pro unlocks here
- The exact prevention SQL — copy-paste ready
- Raw psql output captured from the Docker lab
- A senior-DBA action list to take it further
- Live monitoring queries to catch it in production
- The deeper audit: fix-that-fails counterexample, GUC before/after, server-log evidence
Related & next steps
Follow the thread
Everything this error touches — jump straight to the sibling error, term, runbook, or parameter.
Verification
- Last verified
- 2026-07-15 (Docker lab, PostgreSQL 16.14)
- Reviewed by
- Verified against PostgreSQL 16.14 in an isolated lab environment
- Audit status
- reviewed