Fix connection left in broken state when BEGIN fails or ROLLBACK errors#457
Merged
isoos merged 4 commits intoJun 7, 2026
Merged
Conversation
Two cleanup gaps in runTx: 1. _activeTransaction was assigned before BEGIN entered the try block. If BEGIN was rejected by PostgreSQL the assignment was never unwound, permanently blocking the connection with "inside a runTx call" on every subsequent execute() call. Fix: move BEGIN inside the try block so a failed BEGIN goes through the same cleanup path as any other error. 2. When ROLLBACK itself failed and the original exception was a PgException, the rollback failure was silently swallowed and the connection was returned to the pool in an undefined PostgreSQL transaction state. Fix: call _closeAfterError() on both ROLLBACK call sites when ROLLBACK fails, matching pgx which explicitly kills the connection in this case.
Owner
|
@kartikey321: could you please update the changelog too (for the other PR too)? |
Contributor
Author
|
Hi @isoos, working on the changelog update now. Quick question — would you prefer #455 and #457 kept as separate |
Owner
|
Can be merged, and yes, bump the version to 3.5.12 |
When any PgException occurred inside runTx, _transactionException was set and never cleared — even after a successful ROLLBACK TO SAVEPOINT restored PostgreSQL to a healthy state. mayCommit therefore returned false and the driver sent ROLLBACK instead of COMMIT, silently discarding all subsequent work that completed successfully at the PostgreSQL level. Fix: clear _transactionException in _handleMessage whenever PostgreSQL sends ReadyForQuery with state='T' (InTransaction), which is the authoritative signal that the connection is in a clean, committable state.
isoos
reviewed
Jun 7, 2026
Owner
|
Thanks! I'll release the new version probably tomorrow the latest. |
Contributor
Author
|
Thank you for the quick action |
This was referenced Jun 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #456
Problem
Two cleanup gaps in
runTxleave the connection in an unusable or undefined state after failure scenarios.Bug 1 — Connection permanently blocked when BEGIN fails
_activeTransactionwas assigned beforeBEGINentered thetryblock. If PostgreSQL rejectedBEGIN(e.g. the connectionwas already in a failed transaction state), the assignment was never unwound. Every subsequent
conn.execute()call then threw:Bug 2 — Failed ROLLBACK leaves connection in undefined state
When
ROLLBACKitself failed and the original exception was aPgException, the rollback failure was silently swallowed andthe connection was returned to the pool with the PostgreSQL backend potentially still mid-transaction. Subsequent queries on
that connection would execute inside a ghost transaction.
Fix
Bug 1: Move
BEGINinside thetryblock so a failedBEGINgoes through the same cleanup path (_activeTransaction = null, session closed) as any other error.Bug 2: Call
_closeAfterError()on bothROLLBACKcall sites whenROLLBACKitself fails, matching the behaviour ofpgx which explicitly kills the connection in this case.
Testing
Added a regression test in
test/transaction_test.dartcovering the failedBEGINscenario — manually starting a transaction,triggering an error to put the connection in a failed state, then calling
runTxand verifying the connection remains usableafterwards.