- SQLSTATE
- 25P02
- Condition name
in_failed_sql_transaction- Product
- PostgreSQL
- Versions
- PostgreSQL 13 to 18 (examples run on PostgreSQL 18.6 in PGlite)
- Category
- Transactions & Concurrency
- Last reviewed
- 24 October 2026
On this page
What it means
SQLSTATE 25P02 means an earlier statement in the same transaction failed, so PostgreSQL refuses every further statement until the transaction is ended with ROLLBACK (or rolled back to a savepoint).
Inside a transaction block (after BEGIN, or when a driver or framework has opened a transaction for you), any error puts the whole transaction into an aborted state. PostgreSQL does not undo just the failed statement: the work done so far can no longer be committed, and every following statement, even a plain SELECT 1 or a SAVEPOINT, is rejected with 25P02 (in_failed_sql_transaction). Nothing is wrong with the statement that shows 25P02; it only tells you that something earlier failed and the transaction was not ended.
The way out is ROLLBACK. A COMMIT in an aborted transaction does not save anything either: it ends the transaction and rolls it back. If you need to recover from an expected error and keep the rest of the work, set a SAVEPOINT before the risky statement and ROLLBACK TO SAVEPOINT after it fails. Drivers in autocommit mode run each statement as its own transaction, so they never reach this state; when autocommit is off (the default in Python's psycopg, and inside any framework-managed transaction), the driver opens a transaction implicitly and the same rule applies.
Common causes
- Application code catches an error inside a transaction (a duplicate key, a bad value) and carries on running statements without rolling back.
- A connection is returned to a pool in an aborted transaction, so the next request that uses it fails on its first statement.
- A script run in psql or a GUI tool continues after an error inside BEGIN ... COMMIT, and every later statement reports 25P02.
- A SAVEPOINT is created after the failing statement instead of before it.
- Code assumes that a failed statement is undone on its own, as in some other databases, and keeps the transaction open.
Diagnose it
Read-only These queries only read the system catalogs (pg_catalog, information_schema) and statistics views such as pg_stat_activity and pg_locks. They do not change data, roles or settings, so they are safe to run first.
Find the first error, not the 25P02
Look further back in the application log or the server log for the first ERROR in the same session or transaction: that statement and its SQLSTATE (for example 23505 or 22P02) are what needs fixing. By default the server log also records the statement that caused each error (log_min_error_statement is error).
Sessions stuck in an aborted transaction
Run this from another session: inside the aborted transaction it would fail with 25P02 itself.
SELECT pid, usename, application_name, state, xact_start, state_change,
left(query, 80) AS last_query
FROM pg_stat_activity
WHERE state = 'idle in transaction (aborted)'
ORDER BY xact_start; Each connection that is waiting in an aborted transaction, with the last statement it sent (usually the one that failed). Many rows from one application point to missing ROLLBACK handling or a pool that does not reset connections.
Settings that log errors and limit idle transactions
SELECT name, setting, unit
FROM pg_settings
WHERE name IN ('log_min_error_statement', 'idle_in_transaction_session_timeout'); Whether failing statements are logged, and whether sessions left idle inside a transaction are ended automatically (0 means never).
How to fix it
Each fix is labelled with what it changes. Try read-only checks and the least invasive fix first, and test changes outside production.
ROLLBACK, fix the first error, then retry
Changes database stateEnd the aborted transaction. Everything since BEGIN is discarded, so run the whole unit of work again once the cause of the first error is fixed.
ROLLBACK;
Use a savepoint around a statement that may fail
Changes dataSet the SAVEPOINT before the risky statement. If it fails, ROLLBACK TO SAVEPOINT undoes only that part and the transaction can continue and commit. This sequence was checked statement by statement: the duplicate row is skipped and rows 2 and 3 are committed. For duplicates specifically, INSERT ... ON CONFLICT DO NOTHING avoids the error altogether.
BEGIN;
INSERT INTO accounts VALUES (2, 100);
SAVEPOINT before_risky;
INSERT INTO accounts VALUES (2, 300); -- fails with 23505
ROLLBACK TO SAVEPOINT before_risky; -- the transaction is usable again
INSERT INTO accounts VALUES (3, 50);
COMMIT;
Roll back in application error handling
Client or network sideWherever code catches a database error inside a transaction, roll back before doing anything else on that connection (most drivers have a rollback() method, and frameworks do it when a transaction block exits with an exception). Make sure the connection pool resets connections, so an aborted transaction never reaches the next request. If a statement is allowed to fail, wrap it in a savepoint as shown above.
In psql, let errors roll back automatically while working interactively
Client or network sidepsql's ON_ERROR_ROLLBACK variable makes psql set an implicit savepoint before each statement in a transaction block and roll back to it on error, so a typo does not abort the whole transaction. interactive applies it only to interactive sessions, not to scripts, which should still stop on the first error.
\set ON_ERROR_ROLLBACK interactive
Examples
A duplicate key, then the next statement in the same transaction
Run the statements one at a time, as psql or an application does. The second INSERT fails first with 23505; the error shown is the one the SELECT after it receives.
CREATE TABLE accounts (id int PRIMARY KEY, balance numeric(10,2));
BEGIN;
INSERT INTO accounts VALUES (1, 100);
INSERT INTO accounts VALUES (1, 200); -- ERROR: duplicate key value (23505)
SELECT * FROM accounts; -- this statement gets 25P02
ERROR: current transaction is aborted, commands ignored until end of transaction block
Reproduced on PGlite 0.5.11 (PostgreSQL 18.6 compiled to WebAssembly), 24 October 2026.
COMMIT after the error saves nothing
The error shown is the one the UPDATE receives. COMMIT then ends the aborted transaction without saving anything, so the count afterwards is 0 even though the INSERT itself succeeded.
CREATE TABLE accounts (id int PRIMARY KEY, balance numeric(10,2));
BEGIN;
INSERT INTO accounts VALUES (1, 100);
SELECT 1/0; -- ERROR: division by zero (22012)
UPDATE accounts SET balance = 0; -- this statement gets 25P02
COMMIT;
SELECT count(*) FROM accounts; -- 0: the INSERT was rolled back too
ERROR: current transaction is aborted, commands ignored until end of transaction block
Reproduced on PGlite 0.5.11 (PostgreSQL 18.6 compiled to WebAssembly), 24 October 2026.
A savepoint set after the error is too late
Even SAVEPOINT is refused once the transaction has failed; it has to be set before the statement that may fail. The first error here is 42P01.
BEGIN;
SELECT * FROM missing_table; -- ERROR: relation does not exist (42P01)
SAVEPOINT s1; -- this statement gets 25P02
ERROR: current transaction is aborted, commands ignored until end of transaction block
Reproduced on PGlite 0.5.11 (PostgreSQL 18.6 compiled to WebAssembly), 24 October 2026.
How to prevent it
Treat every database error inside a transaction as the end of that transaction: roll back, then retry or report. Use savepoints (or ON CONFLICT for duplicates) only around statements that are expected to fail, and keep transactions short so an aborted one is noticed quickly.
Configure connection pools to reset connections when they are returned, and consider idle_in_transaction_session_timeout so sessions left open in a transaction, aborted or not, are closed by the server instead of holding locks and connection slots.
Common questions
How do I fix "current transaction is aborted, commands ignored until end of transaction block"?
Run ROLLBACK to end the transaction, then find the first error that came before this one (in your logs or the psql output) and fix that. The statement that reports 25P02 is not the problem.
Can I just COMMIT and keep the statements that worked?
No. Once a transaction has failed, COMMIT ends it with a rollback, so nothing from it is saved. To keep earlier work after an expected failure, set a SAVEPOINT before the risky statement and use ROLLBACK TO SAVEPOINT.
Why does PostgreSQL abort the whole transaction when one statement fails?
A transaction is all or nothing. Rather than guess which part of the work is still valid, PostgreSQL refuses to continue until you either roll back everything or roll back to a savepoint you set for exactly this purpose.
I get 25P02 on the first statement of a request. How is that possible?
The connection came from a pool still inside a transaction that failed during an earlier request. Make the earlier code roll back on errors, and configure the pool to reset connections when they are returned.
Learn more on CodeWithSQL
Official documentation
- PostgreSQL error codes (Appendix A)
- Tutorial: transactions and savepoints
- SAVEPOINT
- psql (ON_ERROR_ROLLBACK)
Last technically reviewed 24 October 2026; examples and diagnostics run on PostgreSQL 18.6 (PGlite 0.5.11). SQL-level examples were run in PGlite (PostgreSQL compiled to WebAssembly); examples that need a running server are shown as documented in the PostgreSQL documentation.