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';
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';
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
Quick Fix Solutions
-
Always start with BEGIN: Ensure every
PREPARE TRANSACTIONcall is preceded by an explicitBEGINstatement. -
Validate transaction state before preparing: Use
txid_current_if_assigned()to confirm an active transaction exists before calling branch transaction commands. - Monitor pg_prepared_xacts: Set up a scheduled job to detect and clean up stale prepared transactions.
-
Set server_reset_query in PgBouncer: Add
DISCARD ALLorROLLBACKto 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
Prevention Tips
-
Centralize 2PC logic: Never scatter raw
PREPARE TRANSACTIONcalls 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_xactsand 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';
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)