PostgreSQL No Active SQL Transaction for Branch Transaction (SQLSTATE 25005)

PostgreSQL raises SQLSTATE 25005 (no_active_sql_transaction_for_branch_transaction) when a distributed transaction management operation attempts to join or associate with a branch transaction that is not currently active. The error appears in logs as:

ERROR:  there is no active transaction
SQLSTATE: 25005

This error is specific to distributed transaction protocols, primarily XA (eXtended Architecture) transactions, and is not encountered in ordinary single-connection transaction usage.

What This Error Means

SQLSTATE class 25 covers invalid transaction state errors in PostgreSQL. The full class includes conditions such as active_sql_transaction (25001), branch_transaction_already_active (25002), and held_cursor_requires_same_isolation_level (25008). Code 25005 specifically signals that a branch join was requested for a global transaction branch that has no active transaction state on the current connection.

In the XA/two-phase commit model, a global transaction is split into branches. Each branch is identified by an XID (transaction identifier) composed of a global transaction ID, a branch qualifier, and a format ID. The distributed transaction manager is expected to issue XA START to open a branch, then execute work, and finally progress through the XA END / XA PREPARE / XA COMMIT or XA ROLLBACK lifecycle. PostgreSQL tracks the state of each branch internally. If a transaction manager tries to use XA JOIN (or an equivalent association command) to re-attach to a branch whose state has already ended, been rolled back, or was never started, PostgreSQL returns 25005.

The error leaves the current connection's transaction state unchanged — it is a protocol-level rejection, not a transaction abort. However, the distributed transaction manager must treat this as a coordination failure and take corrective action (typically rolling back the global transaction).

Common Causes

  1. Joining a branch after XA END has already been called. Once XA END is issued to disassociate from a branch, that branch is no longer active. Calling XA JOIN on an already-ended branch is illegal and triggers 25005.

  2. Race condition or duplicate branch ID. A transaction manager bug or misconfiguration generates the same XID for two separate branches. One branch proceeds normally while the other fails with 25005 because the first branch consumed or altered the shared state.

  3. Branch state lost after a connection reset. If the underlying database connection drops and is replaced (e.g., by a connection pool that silently reconnects), the new connection has no knowledge of branches started on the old connection. Attempting to join a branch on the new connection yields 25005.

  4. Incomplete XA implementation in middleware or driver. Some JDBC drivers, ORM frameworks, or application servers implement XA only partially and issue join commands out of sequence. This is especially common when migrating between application server versions or switching JTA implementations.

How to Fix no_active_sql_transaction_for_branch_transaction

  1. Audit the XA lifecycle in your transaction manager. Confirm that XA START is called before any XA JOIN, and that XA JOIN is never issued after XA END for the same branch. The legal state transitions are:

    XA START xid            -- opens the branch
    -- SQL work --
    XA END xid              -- dissociates; branch is now IDLE
    XA PREPARE xid          -- moves branch to PREPARED
    XA COMMIT xid           -- or XA ROLLBACK xid
    

    Inserting an XA JOIN after XA END is invalid.

  2. Ensure unique branch qualifiers. Each branch within a global transaction must have a distinct branch qualifier. Verify that your transaction coordinator generates unique XIDs and that no two branches share the same identifier:

    -- List in-doubt prepared transactions to check for duplicate XIDs
    SELECT gid, prepared, owner, database FROM pg_prepared_xacts;
    
  3. Do not share XA branches across connection pool connections. XA branch state is connection-local in PostgreSQL. Configure your connection pool so that XA transactions pin a single physical connection for the duration of the branch, preventing the pool from returning a different connection mid-transaction.

  4. Handle XA ROLLBACK on error paths. If your transaction manager catches a prior error and retries without first rolling back the branch, the branch may end up in an unexpected state. Always call XA ROLLBACK xid on any branch that encountered an error before retrying with a fresh XA START:

    XA ROLLBACK 'my-global-txn:branch-1:1';
    
  5. Clean up orphaned prepared transactions. Long-running or crashed distributed transactions can leave branches in a PREPARED state indefinitely. Identify and clean them up:

    -- Find old prepared transactions
    SELECT gid, prepared, owner
    FROM pg_prepared_xacts
    WHERE prepared < NOW() - INTERVAL '10 minutes';
    
    -- Roll back an orphaned branch
    ROLLBACK PREPARED 'my-global-txn:branch-1:1';
    

Additional Information

  • SQLSTATE 25005 belongs to class 25 (Invalid Transaction State). Related sibling codes include:
    • 25000invalid_transaction_state (generic)
    • 25001active_sql_transaction (operation not allowed inside a transaction)
    • 25002branch_transaction_already_active
    • 25003inappropriate_access_mode_for_branch_transaction
    • 25004inappropriate_isolation_level_for_branch_transaction
    • 25006read_only_sql_transaction
    • 25008held_cursor_requires_same_isolation_level
  • PostgreSQL supports two-phase commit via PREPARE TRANSACTION / COMMIT PREPARED / ROLLBACK PREPARED natively. Full XA protocol support (with XA START, XA END, etc.) is typically provided at the JDBC/driver layer (e.g., the PostgreSQL JDBC driver's PGXAConnection) or via extensions.
  • Java EE / Jakarta EE application servers (WildFly, GlassFish, WebLogic) that use JTA over a PostgreSQL XA datasource are the most common environment where 25005 surfaces. Enabling XA transaction logging in the application server is the fastest way to pinpoint the out-of-sequence command.
  • Orphaned prepared transactions (from crashed coordinators) block vacuum on the tables they touched. Long-lived unresolved prepared transactions have performance implications beyond the immediate 25005 error.

Frequently Asked Questions

Why does this error only appear under load or intermittently? Race conditions in the transaction manager — such as two threads operating on the same global transaction ID — are the most frequent cause of intermittent 25005 errors. Under low load, serialized execution masks the problem. Enabling verbose XA logging in your application server will capture the exact sequence of XA commands being issued.

Can this error occur with regular BEGIN / COMMIT transactions? No. SQLSTATE 25005 is specific to distributed branch transaction protocols (XA / two-phase commit). Ordinary single-connection transactions using BEGIN, COMMIT, and ROLLBACK will never produce this error.

How do I confirm whether orphaned prepared transactions are causing the issue? Query pg_prepared_xacts. If you see rows that are hours or days old and owned by a crashed application, those are orphaned. Use ROLLBACK PREPARED 'gid' (as a superuser or the transaction owner) to remove them.

Does PostgreSQL support XA JOIN directly in SQL? PostgreSQL does not expose XA START/XA JOIN/XA END as SQL commands natively. These are handled by client-side drivers (e.g., JDBC PGXAConnection). The 25005 error is raised by PostgreSQL's internal transaction state machine when the driver sends a protocol-level join request for a branch in the wrong state.

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.