When you issue a ROLLBACK, ROLLBACK TO SAVEPOINT, or similar transaction control command outside of any active transaction, PostgreSQL raises:
ERROR: there is no transaction in progress
The SQLSTATE is 25P01 and the condition name is no_active_sql_transaction. PostgreSQL class 25 covers Invalid Transaction State errors — situations where a transaction control statement is not valid given the current transaction state.
What This Error Means
PostgreSQL session state includes a transaction status that is either idle (no active transaction), in transaction, or in failed transaction. The 25P01 error fires when a transaction control command that only makes sense inside a transaction — such as ROLLBACK, ROLLBACK TO SAVEPOINT, or RELEASE SAVEPOINT — is executed while the session is in the idle state.
Issuing ROLLBACK when no transaction is open is a no-op in many database systems, but PostgreSQL raises this as a warning or error depending on the context. In an interactive psql session you will see the message as a WARNING and the command is silently ignored. When issued through a client driver or in a PL/pgSQL function, it surfaces as a proper ERROR that will abort the current operation.
The error does not affect any data — because there was never a transaction in flight, nothing needs to be rolled back. The connection remains usable immediately after the error is raised.
Common Causes
Bare
ROLLBACKafter a successful commit. Application code that uses atry/except/finallypattern sometimes issuesROLLBACKunconditionally in a cleanup block, even after the transaction was already committed. If the commit succeeded, the subsequentROLLBACKfinds no active transaction.Calling
ROLLBACKwhen autocommit is on. Frameworks and connection pool libraries (e.g., SQLAlchemy in non-transactional execution contexts, asyncpg withautocommit=True) do not open an explicit transaction. AnyROLLBACKissued in that mode targets a non-existent transaction.Duplicate
ROLLBACKcalls. Exception-handling logic that callsROLLBACKin multiple code paths can trigger a secondROLLBACKafter the first one already ended the transaction.ROLLBACK TO SAVEPOINTorRELEASE SAVEPOINTwithout a matchingSAVEPOINT. If the savepoint was never created — for example because the code path that creates it was skipped — the subsequent savepoint command has no target.Misconfigured ORM/session lifecycle. An ORM session that was closed, expired, or recycled before the rollback call is made. The underlying database connection is back in idle state while the application still thinks a transaction is open.
How to Fix no_active_sql_transaction
Check whether a transaction is active before rolling back. Many drivers expose a transaction status flag. Use it to guard rollback calls:
# psycopg2 example if conn.status == psycopg2.extensions.STATUS_IN_TRANSACTION: conn.rollback()# asyncpg example if conn.is_in_transaction(): await conn.execute("ROLLBACK")Use
BEGIN ... EXCEPTIONin PL/pgSQL instead of naked ROLLBACK. Inside a function, use subtransactions viaBEGIN / EXCEPTIONblocks rather than issuingROLLBACKexplicitly:DO $$ BEGIN -- your work here EXCEPTION WHEN others THEN -- PL/pgSQL rolls back the subtransaction automatically RAISE NOTICE 'Caught error: %', SQLERRM; END; $$;Restructure cleanup logic to avoid unconditional ROLLBACK. Replace patterns like:
try: conn.execute("BEGIN") # ... do work ... conn.execute("COMMIT") finally: conn.execute("ROLLBACK") # always fires, even after COMMITwith a conditional approach or rely on the driver's context manager:
with conn.transaction(): # driver handles COMMIT on success, ROLLBACK on exception passGuard savepoint commands. Track whether a savepoint was established before attempting to roll back or release it:
-- Only release if savepoint exists SAVEPOINT my_sp; -- ... work ... RELEASE SAVEPOINT my_sp;If the savepoint setup is conditional, mirror that condition before the release.
In psql scripts, use
\set ON_ERROR_STOP oncarefully. In non-interactive scripts, a spuriousROLLBACKwarning can be silenced, but it is better to fix the logic than to hide the symptom.
Additional Information
- SQLSTATE class
25(Invalid Transaction State) includes related conditions:25001(active_sql_transaction— the complement of this error, raised when a statement requires no active transaction),25006(read_only_sql_transaction),25P02(in_failed_sql_transaction), and25008(held_cursor_requires_same_isolation_level). - In an interactive
psqlsession,ROLLBACKwith no active transaction emits aWARNINGrather than anERROR, so scripts running inpsqlmay silently pass while the same code fails when run through a driver. - The error has been part of PostgreSQL since well before version 9; there are no version-specific behavioral changes of note.
- JDBC drivers and psycopg2 surface this as a
PSQLException/psycopg2.InternalErrorwith SQLSTATE25P01. Nodepgraises it as a standardErrorwith.code === '25P01'. - This error carries no performance implications — the statement is rejected immediately and no transaction state is modified.
Frequently Asked Questions
Why does ROLLBACK work fine in psql but fail in my application?
In an interactive psql session, PostgreSQL demotes the error to a warning and silently ignores the command. Client drivers (psycopg2, JDBC, asyncpg, etc.) treat it as a proper error that raises an exception. Always test transaction logic through your actual driver, not just interactively.
Does this error mean data was lost?
No. Because there was no active transaction when ROLLBACK was called, there is nothing to roll back and no data is affected. If data loss occurred, it happened elsewhere — for example, an earlier commit that you did not intend to make.
Can I use ROLLBACK safely as a "just in case" cleanup step?
Not without first checking the transaction status. The safest approach is to use the connection or session object's built-in transaction context manager, which handles commit and rollback automatically based on whether an exception occurred.
What is the difference between SQLSTATE 25P01 and 25P02?
25P01 (no_active_sql_transaction) fires when a transaction command is issued and there is no transaction open. 25P02 (in_failed_sql_transaction) fires when any command other than ROLLBACK is issued while the transaction is already in an error/aborted state. They represent opposite ends of the transaction lifecycle mistake spectrum.