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 25 — Invalid 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
Calling
SET TRANSACTIONafter the first query in a transaction block. ABEGINfollowed immediately by aSELECT,INSERT, or any other statement advances the transaction beyond its starting point. AnySET TRANSACTIONissued after that point triggers25001.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 TRANSACTIONwill fail.ORM or framework-generated transaction setup. Some ORMs (e.g., SQLAlchemy, ActiveRecord, Hibernate) emit a
BEGINand then separately set the isolation level. If the ORM emits any statement betweenBEGINandSET TRANSACTION ISOLATION LEVEL, PostgreSQL will raise25001.Procedural code inside PL/pgSQL issuing
SET TRANSACTION. Inside a PL/pgSQL function called within an outer transaction,SET TRANSACTIONis invalid because the function executes in the context of the already-active outer transaction.
How to Fix active_sql_transaction
Issue
SET TRANSACTIONimmediately afterBEGIN, 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
BEGINandSET TRANSACTION.Use
SET SESSION CHARACTERISTICS AS TRANSACTIONto 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.
Use
START TRANSACTIONwith inline options (PostgreSQL 9.x+). PostgreSQL allows transaction properties to be specified directly in theSTART TRANSACTIONorBEGINstatement:START TRANSACTION ISOLATION LEVEL REPEATABLE READ READ ONLY; -- proceed with your queries COMMIT;This avoids the need for a separate
SET TRANSACTIONcall entirely.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
ROLLBACKin an error handler or a poolreset_on_returnconfiguration option can help.Avoid
SET TRANSACTIONinside 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:25000—invalid_transaction_state(generic)25001—active_sql_transaction(this error)25002—branch_transaction_already_active25008—held_cursor_requires_same_isolation_level25P01—no_active_sql_transaction25P02—in_failed_sql_transaction25P03—idle_in_transaction_session_timeout(PostgreSQL 9.6+)
- This error has been present since early PostgreSQL versions; the behavior of
SET TRANSACTIONis defined by the SQL standard and has not changed significantly. - Python's
psycopg2andpsycopg3surface this aspsycopg2.errors.ActiveSqlTransaction(a subclass ofInternalError). SQLAlchemy may wrap it in ansqlalchemy.exc.InternalError. - Node.js
pg(node-postgres) surfaces it as anErrorwithcode: '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.