XACT_E_ABORTING (0x8004D029): Local transaction aborted fix
Your transaction was killed by another process or timeout. Here's how to find the culprit and stop it from happening again.
Quick answer
Restart the Distributed Transaction Coordinator service and disable network DTC access if you don't need it. Then check which process called ITransaction::Abort on your transaction — that's what triggered 0x8004D029.
What's actually happening here
Error 0x8004D029 (XACT_E_ABORTING) means your local transaction was already in the process of being aborted when you tried to commit or do more work on it. The transaction didn't fail because of your code — it was killed externally. The two most common killers: a timeout from the Microsoft Distributed Transaction Coordinator (MSDTC) or another thread or process calling ITransaction::Abort on the same transaction object.
I've seen this most often in three scenarios:
- A COM+ component holding a transaction open longer than the configured timeout (usually 60 seconds).
- A stored procedure that calls
ROLLBACKinside a nested transaction — the outer transaction then sees an aborted state. - A client-side timeout kills the connection while a transaction is still active, and when the connection pool reuses it, you get this error.
Step-by-step fix
Step 1: Find the culprit process
Before changing anything, figure out who's killing your transaction. Open Event Viewer and look under Applications and Services Logs > Microsoft > Windows > TransactionCoordinator. Filter for Event ID 4097 or 4098 — those log transaction aborts with the caller's process ID.
If you don't see anything there, enable DTC tracing:
msdtc -uninstall
msdtc -install
net stop msdtc
net start msdtcThen run dxdiag and enable DTC logging under the DTC tab. Reproduce the error and check the trace log at %SystemRoot%\System32\MsDtc\Trace. Look for the string Aborting — it'll show the calling application's PID.
Step 2: Kill the timeout (the real fix)
Defaults are too short for any real work. Change the DTC timeout from 60 seconds to something sane. Open Component Services (dcomcnfg.exe), then Component Services > Computers > My Computer > Distributed Transaction Coordinator > Local DTC. Right-click Local DTC, go to Properties > Security.
Uncheck Network DTC Access unless you're actually doing distributed transactions across machines. Nine times out of ten, people don't need it and it just adds latency and failure points. If you do need it, leave it checked but set the timeout under Transaction Manager Communication > Advanced > Communication Timeout (seconds) to at least 120.
Step 3: Check your connection pooling
If steps 1 and 2 don't help, the issue is likely connection pooling reusing a busted connection. In your application code, when you catch an XACT_E_ABORTING error, you must explicitly rollback the transaction and close the connection — don't return it to the pool.
In .NET:
try {
transaction.Commit();
}
catch (TransactionAbortedException ex) {
transaction.Rollback();
connection.Close();
// Let the pool drop this connection
}The reason step 3 works is that the connection's internal transaction context is corrupt after the abort. If you just close it normally and return it to the pool, the next use gets the same corrupted state and throws the same error again.
Alternative fixes if the main one fails
- Reboot the machine. I'm serious — DTC leaks kernel handles. A machine that's been up for 6+ months will start throwing random transaction errors. Schedule monthly restarts for any server doing heavy transaction processing.
- Disable Transaction Manager voting. In the DTC properties, go to Transaction Manager Communication and set Transaction Manager Voting to Loose. This prevents slow participants from aborting your entire transaction.
- Switch to lightweight transactions. If you're using System.Transactions in .NET, set
TransactionScopewithTransactionScopeAsyncFlowOption.Enabledand ensure the transaction escalates to a distributed transaction only when absolutely necessary. UseEnlist=falsein your connection string for connections that don't need to participate.
Prevention tip
Don't let transactions live longer than a few seconds. If you have a transaction that spans multiple database calls or includes user input, refactor it. Your transaction should only wrap the atomic database write, not the business logic. Use a pattern like:
- Validate all inputs and preconditions outside the transaction.
- Open a new transaction.
- Do all writes in sequence.
- Commit immediately.
If you find yourself keeping a transaction open while waiting for a user to click OK, you've already lost. The DTC will time out and kill it for you — and you'll be right back at 0x8004D029.
Was this solution helpful?