Repository navigation
fix: report errors from msnodesqlv8 commit and rollback - #1913
Merged
dhensby merged 1 commit intoOct 13, 2026
Merged
Conversation
dhensby
force-pushed
the
fix/msnodesqlv8-transaction-errors
branch
from
October 5, 2026 08:02
695b760 to
3a3ee8f
Compare
On msnodesqlv8, commit() and rollback() wrapped the server's error in a TransactionError and then dropped it, so both resolved whatever the server said. Once the server had already ended a transaction (XACT_ABORT on plus a failing statement, say), commit() resolved and emitted 'commit' although nothing had been committed, and rollback() resolved silently. rollback() also ran commit()'s checks, so calling it while a request was in progress failed with "Can't commit transaction". Pass the TransactionError on, so commit() and rollback() reject when the server refuses them, as they do on tedious, and run the rollback checks in rollback(). Whenever the server has already rolled the transaction back on msnodesqlv8, because the caller turned XACT_ABORT on or because the transaction was chosen as a deadlock victim, rollback() now rejects with a TransactionError instead of resolving. The README's aborted transactions section now describes both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dhensby
force-pushed
the
fix/msnodesqlv8-transaction-errors
branch
from
October 13, 2026 11:48
3a3ee8f to
77a8dd8
Compare
There was a problem hiding this comment.
🟢 Approval recommended
The focused implementation correctly propagates errors, preserves cleanup, and includes appropriate cross-driver coverage.
0 open findings
What changed in this PR
Fixes msnodesqlv8 transaction finalization so commit and rollback server errors propagate correctly.
Changes:
- Propagates wrapped transaction errors and uses the correct rollback guard.
- Adds shared cross-driver integration coverage.
- Documents msnodesqlv8 behavior after server-side rollback.
| File | Description |
|---|---|
lib/msnodesqlv8/transaction.js |
Returns commit/rollback errors and calls the rollback base hook. |
test/common/tests.js |
Adds shared transaction failure tests. |
test/tedious/tedious.js |
Registers the new tests for tedious. |
test/msnodesqlv8/msnodesqlv8.js |
Registers the new tests for msnodesqlv8. |
README.md |
Documents msnodesqlv8 server-side rollback behavior. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
🎉 This PR is included in version 12.7.5 🎉 The release is available on: Your semantic-release bot 📦🚀 |
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.
Problem
In
lib/msnodesqlv8/transaction.js,_commitand_rollbackwrap the server's error in aTransactionErrorand then callcallback(null), so both always resolve. This has been the case since v4.0.0 (5e26731).XACT_ABORT ONplus a failing statement, the server rolls the transaction back.commit()then resolves and emits'commit', although nothing was committed: the server answeredCOMMIT TRANSACTIONwith error 3902. tedious rejects withEABORThere.rollback()resolves (and emitsrollback(false)) even though the server answered with error 3903._rollbackcallssuper._commit, so it rejects with "Can't commit transaction. There is a request in progress." (tedious says "Can't rollback…"). It also never sets_rollbackRequested; only the tedious driver reads that flag, so this one is cosmetic on msnodesqlv8.Fix
_commitand_rollback.super._rollbackfrom_rollback.The base class needs no change:
'commit'/'rollback'events, and the matchingmssql:transaction:commit/rollbackdiagnostics channel messages, are only emitted on success. They are no longer emitted for a failed commit or rollback on msnodesqlv8.rollback()getsENOTBEGUN.The error is a
TransactionErrorwith:EREQUESTRequestErrorasoriginalError(number 3902/3903 in the cases above)On tedious the same situation gives
EABORT, because tedious sees the server's rollback (ENVCHANGE) and marks the transaction aborted. The class matches across drivers; the code does not.Docs
README, "Aborted transactions": that section is written around
abortTransactionOnError, which is a tedious-only option. It now has one paragraph for msnodesqlv8: the driver can't tell when the server has rolled a transaction back, so whenXACT_ABORTis on, or the server rolls the transaction back for another reason (such as a deadlock), therollbackevent isn't emitted and the example'srollback()call fails with aTransactionError.Changelog caveat
On msnodesqlv8,
rollback()now rejects with aTransactionError(codeEREQUEST, "The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION",originalError.number3903) whenever the server has already rolled the transaction back. It used to resolve silently and emitrollback(false). This affects:XACT_ABORT ONthemselves and follow the README's "Aborted transactions" pattern;XACT_ABORTis set to. Acatch (e) { await tran.rollback(); throw e }block will now throw the rollback'sTransactionErrorinstead of the deadlock error, as it already does on tedious (EABORT);rollback tranin a request) and then calledcommit().commit()now rejects with 3902.The new behaviour is correct and close to tedious's
EABORT, but users will see it.Tests
Two new shared tests in
test/common/tests.js, registered as three tests in both suites:commit rejects when the server has already ended the transactionandrollback rejects when the server has already ended the transaction(one shared test, run with each method)rollback while a request is in progress reports a rollback errorDesign notes:
exec sp_executesql N'set xact_abort on; select 1 / 0'. Setting the option insidesp_executesqlscopes it to that batch, so the result doesn't depend on the suite'sabortTransactionOnError. It also leaves the pooled connection's own setting untouched, which matters for msnodesqlv8, where a parameterless query is a plain batch.instanceof sql.TransactionError, since that is all both drivers agree on.Verification:
npm run lintpasses;npm run test-unit: 139 passing.user defined types(no CLR on the local Azure SQL Edge).lib/msnodesqlv8code ran over a tedious-backed stand-in for the native module. Without the lib fix, all three new tests fail; with it, they pass. The other emulated failures are emulator or local-database limits (bulk/bcp, timeouts, cancel, streaming, CLR) and appear identically with and without the fix.transaction with synchronous error, which the msnodesqlv8 parity change registers, passes with this fix on the emulator, run both before and after the XACT_ABORT test.SET XACT_ABORT ONrolls the transaction back and leaves the session's own setting as it was./rollback/match is against node-mssql's own message.Found while bringing the two driver suites in line (#1908, #1910).
🤖 Generated with Claude Code