XACT_E_CANTRETAIN (0X8004D001): Retaining Commit/Abort Not Supported
This COM+ error means your app tried to do a retaining commit or abort on a transaction, but the transaction manager doesn't support it. We'll fix it by adjusting code or the MSDTC settings.
1. Your COM+ component is trying to retain the transaction
This error trips up a lot of developers building COM+ applications in Windows Server or even Windows 10/11 Pro. I remember the first time I hit it—spent a whole afternoon chasing a ghost. The deal is: your component called IObjectContext::SetComplete or SetAbort with the fRetaining flag set to TRUE, but the root transaction object doesn't allow that.
The short version: COM+ transactions are meant to be either committed or aborted once, and that's it. You cannot do a partial commit and keep the transaction alive for more work. That's what "retaining" means—it tries to keep the transaction context after committing or aborting, like a database nested transaction. But the Distributed Transaction Coordinator (MSDTC) says no.
How to fix it
Open your COM+ component code—usually C++, C#, or VB.NET that uses the COM+ Services (System.EnterpriseServices). Look for any call to ContextUtil.SetComplete or ContextUtil.SetAbort. In C++, it's IObjectContext::SetComplete or IObjectContext::SetAbort. The fRetaining parameter defaults to FALSE, but someone might have explicitly set it to TRUE.
Here's a C# example that triggers the error:
// Wrong: tries to retain the transaction
ContextUtil.SetComplete(); // Default is fRetaining=false, but if you see this in old code...
// In C++: pObjectContext->SetComplete(TRUE); // That's the culprit
The fix is straightforward: always pass FALSE for fRetaining, or don't pass it at all (the default is FALSE). In C#, ContextUtil.SetComplete() already does non-retaining. If you're using ContextUtil.EnableCommit or DisableCommit, those also don't retain—they just hint at the transaction outcome.
Also check if your component is using TransactionOption set to RequiresNew or Required. If it's set to NotSupported, transactions won't flow at all, and you might see a different error. For XACT_E_CANTRETAIN, you need the transaction to be active and the call to be a retaining call.
Real-world trigger: This happens often when someone ports an MTS (Microsoft Transaction Server) component from Windows NT 4.0, where retaining was allowed in some configurations. On modern Windows (Server 2016, 2019, 2022, Windows 10/11), it's blocked.
2. MSDTC security configuration is too restrictive
Even if your code is clean, the MSDTC might be configured to reject retaining operations. This is less common but I've seen it on locked-down servers where the administrator disabled all advanced transaction features.
Here's the thing: MSDTC has a setting called Transaction Manager Communication that can restrict what types of transactions are allowed. The specific flag that blocks retaining is the Allow Only Secure Connections combined with No Retaining option in the registry.
How to check and fix it
Open Component Services (dcomcnfg.exe), then navigate to Component Services > Computers > My Computer > Distributed Transaction Coordinator > Local DTC. Right-click and choose Properties.
In the Security tab, ensure these settings are checked:
- Network DTC Access — unchecked is fine if you're not doing distributed transactions, but if it's checked, make sure Allow Remote Clients and Allow Remote Administration are set appropriately for your environment.
- Transaction Manager Communication — under Authentication Level, pick No Authentication Required for testing, but for production use Mutual Authentication Required. The key is that neither option explicitly blocks retaining commits.
If that doesn't help, check the registry directly. Open Regedit and go to:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSDTC\Security
Look for a DWORD value named AllowOnlySecureConnections. If it's set to 1, set it to 0 and restart the MSDTC service. Also check DisableRetain—if it exists and is 1, delete it or set to 0. Then restart the service with:
net stop msdtc && net start msdtc
Be careful: Changing MSDTC security can break other distributed transaction applications. Only do this if you're sure the code fix didn't work.
3. The component is using an outdated transaction interface
Here's a more obscure one: your code might be using the older ITransaction interface directly instead of the newer IConnectionPoint or IObjectContext from COM+. If you're working with raw ITransaction::Commit or Abort with the grfTC flags set to XACTTC_RETAIN_COMMIT (0x0001) or XACTTC_RETAIN_ABORT (0x0002), you'll get this error because MSDTC doesn't support those flags for root transactions.
The fix is to stop using raw ITransaction and let COM+ manage the transaction lifecycle. In C++, use IObjectContext methods. In .NET, stick with System.EnterpriseServices. If you really need to control the transaction manually, use System.Transactions.TransactionScope instead—it's newer and doesn't have this limitation.
But honestly? You shouldn't be calling ITransaction::Commit at all in a COM+ component. That's the COM+ runtime's job. Let it handle the two-phase commit protocol. Writing your own commit logic is like trying to hand-crank a car that has a starter motor—you'll only break things.
Quick-reference summary table
| Cause | Fix | Check first |
|---|---|---|
| Retaining commit/abort in code | Remove fRetaining=TRUE from SetComplete/SetAbort | Scan code for SetComplete/SetAbort with TRUE parameter |
| MSDTC security blocks retaining | Adjust DTC Security settings or registry keys | Open Component Services > DTC Properties > Security tab |
| Using raw ITransaction interface | Switch to IObjectContext or TransactionScope | Check for direct ITransaction::Commit calls in code |
The most likely culprit is #1—code that explicitly retains the transaction. Nine times out of ten, that's the fix. Change the flag, rebuild, and you're done. If not, dig into MSDTC config. Good luck—you've got this.
Was this solution helpful?