DEV Community

umzzil nng
umzzil nng

Posted on Originally published at oraerror.com

PostgreSQL 25005 Error: Causes and Solutions Complete Guide

PostgreSQL Error 25005: no active sql transaction for branch transaction

PostgreSQL error code 25005 occurs when a branch transaction command — most commonly PREPARE TRANSACTION — is executed without an active SQL transaction block in the current session. This error is closely tied to Two-Phase Commit (2PC) workflows and distributed transaction management. If your application or middleware calls PREPARE TRANSACTION outside of an explicit BEGIN/START TRANSACTION block, PostgreSQL will immediately raise this error.


Top 3 Causes

1. Calling PREPARE TRANSACTION Without BEGIN

The most common cause is simply missing an explicit transaction block before issuing PREPARE TRANSACTION. In autocommit mode, each statement is its own transaction and is already committed before PREPARE TRANSACTION can be applied.

-- ❌ Wrong: No active transaction block
PREPARE TRANSACTION 'txn_order_001';
-- ERROR: 25005 - no active sql transaction for branch transaction

-- ✅ Correct: Wrap in an explicit BEGIN block
BEGIN;
  INSERT INTO orders (customer_id, amount) VALUES (42, 9900);
  UPDATE inventory SET qty = qty - 1 WHERE product_id = 7;
PREPARE TRANSACTION 'txn_order_001';

-- Later, commit or roll back the prepared transaction
COMMIT PREPARED 'txn_order_001';
-- or
ROLLBACK PREPARED 'txn_order_001';
Enter fullscreen mode Exit fullscreen mode

2. Issuing Branch Commands After Transaction Already Closed

Application logic bugs can cause PREPARE TRANSACTION to be called after the transaction has already been committed or rolled back, leaving no active transaction for PostgreSQL to prepare.

-- ❌ Wrong: Transaction already ended before PREPARE
BEGIN;
  INSERT INTO payments (order_id, amount) VALUES (101, 3000);
COMMIT; -- Transaction is now closed

PREPARE TRANSACTION 'txn_payment_101';
-- ERROR: 25005 - no active sql transaction for branch transaction

-- ✅ Correct: PREPARE must come before COMMIT/ROLLBACK
BEGIN;
  INSERT INTO payments (order_id, amount) VALUES (101, 3000);
PREPARE TRANSACTION 'txn_payment_101';
-- Transaction is now in "prepared" state, not yet committed

-- Finalize from a transaction manager
COMMIT PREPARED 'txn_payment_101';
Enter fullscreen mode Exit fullscreen mode

3. Connection Pooling State Mismatch

When using connection poolers like PgBouncer in transaction pooling mode, a reused connection may carry an inconsistent transaction state, causing branch transaction commands to fail because no valid transaction is active on the backend connection.

-- Check for orphaned prepared transactions lingering in the system
SELECT
  gid,
  prepared,
  owner,
  database,
  EXTRACT(EPOCH FROM (now() - prepared)) / 60 AS age_minutes
FROM pg_prepared_xacts
ORDER BY prepared ASC;

-- Clean up orphaned prepared transactions older than 10 minutes
-- (Run this in a monitoring/maintenance job)
ROLLBACK PREPARED 'orphaned_txn_gid_here';

-- Check current max_prepared_transactions setting
SHOW max_prepared_transactions;
-- Must be > 0 to allow any prepared transactions
Enter fullscreen mode Exit fullscreen mode

Quick Fix Solutions

  1. Always start with BEGIN: Ensure every PREPARE TRANSACTION call is preceded by an explicit BEGIN statement.
  2. Validate transaction state before preparing: Use txid_current_if_assigned() to confirm an active transaction exists before calling branch transaction commands.
  3. Monitor pg_prepared_xacts: Set up a scheduled job to detect and clean up stale prepared transactions.
  4. Set server_reset_query in PgBouncer: Add DISCARD ALL or ROLLBACK to your pooler's reset query to clear leftover transaction state between client connections.
-- Quick state check before issuing PREPARE TRANSACTION
SELECT txid_current_if_assigned() IS NOT NULL AS is_in_transaction;
-- Returns true only if inside an active transaction block
Enter fullscreen mode Exit fullscreen mode

Prevention Tips

  • Centralize 2PC logic: Never scatter raw PREPARE TRANSACTION calls across application code. Use a dedicated transaction manager class or service that enforces the correct lifecycle: BEGIN → work → PREPARE → COMMIT/ROLLBACK PREPARED.
  • Enable monitoring alerts: Regularly query pg_prepared_xacts and alert when any prepared transaction is older than a defined threshold (e.g., 5 minutes). Stale prepared transactions can block autovacuum and cause table bloat in addition to surfacing 25005 errors.
-- Example monitoring query for alerting
SELECT COUNT(*) AS stale_prepared_txns
FROM pg_prepared_xacts
WHERE prepared < now() - INTERVAL '5 minutes';
Enter fullscreen mode Exit fullscreen mode

Related Errors

Code Name Brief Description
25000 invalid_transaction_state Parent class for all transaction state errors
25001 active_sql_transaction Opposite of 25005 — command not allowed inside a transaction
25P01 no_active_sql_transaction General version of 25005, not limited to branch transactions
40001 serialization_failure Serialization conflict, common in 2PC environments

📖 Want a more detailed guide?
Check out the full in-depth version (Korean) on oraerror.com — includes detailed analysis, additional SQL examples, and prevention tips.

Top comments (0)