25P02ERRORTier 1 — Safe✅ HIGH confidencecurrent transaction is aborted, commands ignored until end of transaction block
What this means
A previous statement in the current transaction block raised an error and aborted the transaction. Postgres refuses to execute any further statements until the transaction is explicitly rolled back with ROLLBACK or ROLLBACK TO SAVEPOINT.
Why it happens
- 1A statement inside a BEGIN...COMMIT block raised any error (even 22001 or 42703)
- 2The application continues sending queries after catching an error without rolling back
- 3A PL/pgSQL function raised an exception that was not caught within a sub-block
How to reproduce
A failed statement inside a transaction leaves it in an aborted state; a subsequent statement triggers 25P02.
BEGIN;
INSERT INTO users (email) VALUES (NULL); -- raises 23502 (not-null violation)
-- Transaction is now aborted; next statement triggers 25P02:
SELECT * FROM users;Fix 1: ROLLBACK and retry the transaction
When the failed statement was not critical and the transaction should start fresh.
ROLLBACK;
BEGIN;
INSERT INTO users (email) VALUES ('alice@example.com');
SELECT * FROM users;
COMMIT;Why this works
ROLLBACK discards all changes made in the aborted transaction and releases all locks. The transaction state machine returns to IDLE, allowing new transactions to begin. The next BEGIN starts a fresh snapshot.
Fix 2: Use SAVEPOINT to recover from errors within a transaction
When partial work must be preserved and only the failed sub-operation should be rolled back.
BEGIN;
INSERT INTO audit_log (action) VALUES ('batch start');
SAVEPOINT before_risky;
INSERT INTO users (email) VALUES (NULL); -- will fail
ROLLBACK TO SAVEPOINT before_risky; -- undo only the failed insert
INSERT INTO users (email) VALUES ('alice@example.com'); -- retry with valid data
COMMIT;Why this works
SAVEPOINT creates a sub-transaction snapshot. ROLLBACK TO SAVEPOINT restores the transaction state to the savepoint without rolling back the entire transaction. The outer transaction resumes in a valid (non-aborted) state, and subsequent statements execute normally.
What not to do
Continue sending queries after an error hoping they will execute
Why it's wrong: All statements after the first error in a transaction block are discarded until ROLLBACK; no work is done and the connection is blocked.
Sources
📚 Official docs: https://www.postgresql.org/docs/current/errcodes-appendix.html
📚 Feature docs: https://www.postgresql.org/docs/current/tutorial-transactions.html
🔧 Source ref: src/backend/tcop/postgres.c — TransactionBlockStatusCode()
📖 Further reading: Transactions
📖 Further reading: SAVEPOINT
Confidence assessment
✅ HIGH confidence
Stable and well-documented. The transaction state machine is fundamental to Postgres and has not changed. Edge case: in autocommit mode (outside a BEGIN block) each statement is its own transaction and 25P02 cannot occur; it only appears when using explicit transaction blocks.
See also
🔗 Related errors
📄 Reference pages