PostgreSQL Active SQL Transaction (SQLSTATE 25001)

When PostgreSQL raises SQLSTATE 25001, you will see an error like:

ERROR:  25001: SET TRANSACTION ISOLATION LEVEL must be called before any query

or in some contexts:

ERROR:  25001: transaction isolation level may not be changed midway through a transaction

The condition name is active_sql_transaction. It belongs to SQLSTATE class 25Invalid Transaction State — and signals that a command which is only valid outside of an active transaction was issued while a transaction was already in progress.

What This Error Means

SQLSTATE class 25 covers errors related to invalid transaction states. The specific code 25001 (active_sql_transaction) is raised when PostgreSQL receives a statement that must be executed before any transaction work has begun, but a transaction is already underway.

The most common trigger is SET TRANSACTION ISOLATION LEVEL or SET TRANSACTION READ ONLY / SET TRANSACTION READ WRITE. PostgreSQL requires these SET TRANSACTION modifiers to appear at the very start of a transaction — before any DML, queries, or other statements that cause the transaction to enter an active state. Once the first statement in a transaction has executed, the transaction properties are locked in and cannot be changed.

The connection is not terminated when this error is raised, but the current transaction is left in an error state. In PostgreSQL, once an error occurs inside a transaction block, the transaction is marked aborted and all subsequent commands will fail with ERROR: current transaction is aborted, commands ignored until end of transaction block until you issue a ROLLBACK.

Common Causes

  1. Calling SET TRANSACTION after the first query in a transaction block. A BEGIN followed immediately by a SELECT, INSERT, or any other statement advances the transaction beyond its starting point. Any SET TRANSACTION issued after that point triggers 25001.

  2. Application code reusing a connection with an already-open transaction. Connection poolers or application frameworks sometimes reuse database connections without ensuring the previous transaction was cleanly committed or rolled back. If application code assumes the connection is idle but a transaction is still open, a subsequent SET TRANSACTION will fail.

  3. ORM or framework-generated transaction setup. Some ORMs (e.g., SQLAlchemy, ActiveRecord, Hibernate) emit a BEGIN and then separately set the isolation level. If the ORM emits any statement between BEGIN and SET TRANSACTION ISOLATION LEVEL, PostgreSQL will raise 25001.

  4. Procedural code inside PL/pgSQL issuing SET TRANSACTION. Inside a PL/pgSQL function called within an outer transaction, SET TRANSACTION is invalid because the function executes in the context of the already-active outer transaction.

How to Fix active_sql_transaction

  1. Issue SET TRANSACTION immediately after BEGIN, before any other statement.

    BEGIN;
    SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
    -- now execute your queries
    SELECT * FROM orders WHERE status = 'pending';
    COMMIT;
    

    There must be no statements between BEGIN and SET TRANSACTION.

  2. Use SET SESSION CHARACTERISTICS AS TRANSACTION to set defaults at session level. If you consistently need a non-default isolation level, set it once for the session rather than per transaction:

    SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;
    

    This applies to all subsequent transactions on the connection and does not need to be inside a transaction block.

  3. Use START TRANSACTION with inline options (PostgreSQL 9.x+). PostgreSQL allows transaction properties to be specified directly in the START TRANSACTION or BEGIN statement:

    START TRANSACTION ISOLATION LEVEL REPEATABLE READ READ ONLY;
    -- proceed with your queries
    COMMIT;
    

    This avoids the need for a separate SET TRANSACTION call entirely.

  4. Audit connection pool behavior. If the error appears intermittently in an application, check whether the connection pool is returning connections with an open transaction. Ensure every code path in your application either commits or rolls back before returning a connection to the pool. Adding a ROLLBACK in an error handler or a pool reset_on_return configuration option can help.

  5. Avoid SET TRANSACTION inside stored functions. If you need different isolation for a subset of work, restructure the application so the isolation level is set in the outermost transaction block, or use a dedicated connection for that operation.

Additional Information

  • SQLSTATE class 25 (Invalid Transaction State) includes several related codes:
    • 25000invalid_transaction_state (generic)
    • 25001active_sql_transaction (this error)
    • 25002branch_transaction_already_active
    • 25008held_cursor_requires_same_isolation_level
    • 25P01no_active_sql_transaction
    • 25P02in_failed_sql_transaction
    • 25P03idle_in_transaction_session_timeout (PostgreSQL 9.6+)
  • This error has been present since early PostgreSQL versions; the behavior of SET TRANSACTION is defined by the SQL standard and has not changed significantly.
  • Python's psycopg2 and psycopg3 surface this as psycopg2.errors.ActiveSqlTransaction (a subclass of InternalError). SQLAlchemy may wrap it in an sqlalchemy.exc.InternalError.
  • Node.js pg (node-postgres) surfaces it as an Error with code: '25001' on the error object.
  • Java JDBC drivers expose this via SQLException.getSQLState() returning "25001".

Frequently Asked Questions

Why does SET TRANSACTION work in one place but fail in another? PostgreSQL only accepts SET TRANSACTION before the first statement in a transaction block. If anything else executes after BEGIN — even a SELECT 1 or a SET of an unrelated GUC — PostgreSQL considers the transaction active, and a subsequent SET TRANSACTION will raise 25001. The fix is always to ensure SET TRANSACTION is the very first statement after BEGIN.

Can I change the isolation level mid-transaction? No. PostgreSQL does not allow changing transaction properties once a transaction is active. If you need a different isolation level, you must commit or roll back the current transaction and open a new one with the desired level set before executing any statements.

Why does this error appear with my ORM but not in psql? Many ORMs manage transactions automatically and may emit housekeeping statements between BEGIN and the application-level SET TRANSACTION. For example, some versions of SQLAlchemy emit a SAVEPOINT or a SELECT to check the connection before the application code runs. Check your ORM's transaction setup documentation and configure it to emit SET TRANSACTION as part of the BEGIN rather than as a separate step. In SQLAlchemy, the execution_options(isolation_level=...) approach on the connection or engine handles this correctly.

What happens to the transaction after this error is raised? The transaction is marked as aborted. All further SQL commands within the block will fail with ERROR: current transaction is aborted, commands ignored until end of transaction block. You must issue ROLLBACK (or ROLLBACK TO SAVEPOINT if you used a savepoint) to clear the error state before the connection can be used again.

Subscribe to the Pulse Newsletter

Get early access to new Pulse features, insightful blogs & exclusive events , webinars, and workshops.

We use cookies to provide an optimized user experience and understand our traffic. To learn more, read our use of cookies; otherwise, please choose 'Accept Cookies' to continue using our website.