Skip to content

fix: preserve originalError on EABORT TransactionError#1850

Open
dhensby wants to merge 1 commit intotediousjs:masterfrom
dhensby:fix/tx-original-error
Open

fix: preserve originalError on EABORT TransactionError#1850
dhensby wants to merge 1 commit intotediousjs:masterfrom
dhensby:fix/tx-original-error

Conversation

@dhensby
Copy link
Copy Markdown
Collaborator

@dhensby dhensby commented Apr 24, 2026

Problem

When a transaction is aborted by the server (e.g. due to XACT_ABORT or a deadlock), subsequent commit() or rollback() calls return a TransactionError with code EABORT. However, originalError was always undefined on these errors because the error was constructed with a string message rather than an Error object.

This made it difficult for consumers to understand why the transaction was aborted without correlating errors manually.

Closes #1716

Solution

  1. Capture the actual request error — When a request completes with an error and the transaction has been aborted, the error is stored as _abortReason on the transaction. This is done in the tedious request completion handlers (_query, _execute, _bulk).

  2. Generic fallback — The tedious _abort handler (fired by the rollbackTransaction event) sets a generic "Transaction was rolled back by the server" fallback reason for edge cases where the specific error cannot be captured (e.g. connection-level errors).

  3. Attach as originalError — A new _createAbortError() helper on the base Transaction class constructs the EABORT TransactionError and attaches _abortReason as originalError using Object.defineProperty (matching the pattern used elsewhere in MSSQLError).

Changes

  • lib/base/transaction.js — Added _abortReason tracking, _createAbortError() helper, reset on _begin()
  • lib/tedious/transaction.js — Generic fallback _abortReason in _abort handler
  • lib/tedious/request.js — Capture actual request error as abort reason in _query, _execute, _bulk completion paths
  • test/common/unit.js — 4 new unit tests for EABORT originalError behavior

Example

const tx = pool.transaction()
await tx.begin()
try {
  // This query triggers XACT_ABORT
  await tx.request().query("INSERT INTO ...")
} catch (requestErr) {
  // requestErr is the original SQL error
}
try {
  await tx.rollback()
} catch (abortErr) {
  // abortErr.code === "EABORT"
  // abortErr.originalError === requestErr  ← previously undefined
}

When a transaction is aborted (e.g. by XACT_ABORT or deadlock), subsequent
commit() or rollback() calls return a TransactionError with code EABORT.
Previously, originalError was always undefined on these errors because the
error was constructed with a string message rather than an Error object.

Now the actual request error that triggered the abort is captured and
attached as originalError on the EABORT TransactionError. A generic
fallback is used when the specific error cannot be captured (e.g.
connection-level errors).

Closes tediousjs#1716

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dhensby dhensby added this to the v12.x milestone Apr 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

originalError undefined in some transaction errors

1 participant