This one shows up when a COM+ serviced component finishes executing inside a transaction and never votes. The runtime expects a commit or abort call before you leave the method, and it doesn't get one. So it kills the transaction and hands you 0x8004E031. You'll see it in Event Viewer under COM+, in a .NET app calling a ServicedComponent, or in a VB6 component running under the Component Services MMC snap-in. The classic trigger: a method decorated with [AutoComplete] that throws an exception the runtime can't map, or a hand-rolled COM+ component where somebody forgot the exit path.
What's actually going on
COM+ runs your method inside a transaction context. When the method returns, COM+ asks the context a single question: did you finish? The answer comes from SetComplete() or SetAbort() on the object context. If neither call happens, COM+ can't tell whether the work succeeded, so it aborts the transaction. That abort is the error you see.
In .NET this is usually wrapped up by System.EnterpriseServices. You either use [AutoComplete] on the class and let the runtime call SetAbort on exception, or you call ContextUtil.SetComplete() / ContextUtil.SetAbort() yourself. Miss both paths and you get this error. In VB6 COM+ components, it's the same story with ObjectContext.SetComplete and ObjectContext.SetAbort.
The error isn't about DTC being down or MSDTC being misconfigured. People waste hours chasing DTC. The culprit here is almost always the component code path that skips the vote.
The fix
- Find the exact method that fires the error. Turn on COM+ tracing or check the Event Viewer under Windows Logs > Application. Look for the source
COM+orCOMSVCSand note the CLSID and method name. Don't guess — get the real method. - Check the class attribute. If it's a .NET serviced component, look at the class declaration. If
[AutoComplete]is missing and you're not callingContextUtil.SetComplete()orSetAbort(), that's your bug. Add[AutoComplete]and let the runtime handle the vote, or call the ContextUtil methods explicitly. - Look at every exit path. Exceptions, early returns,
gotostatements, swallowed errors intry/catchblocks — any path that leaves the method without voting will trigger this. Wrap the method body intry/finallyand vote in the finally block if you're doing it manually.[Transaction(TransactionOption.Required)] [AutoComplete] public class OrderProcessor : ServicedComponent { public void ProcessOrder(Order o) { // work here // if an exception bubbles up, AutoComplete calls SetAbort // if we return clean, AutoComplete calls SetComplete } } - If you're calling ContextUtil by hand, do it right. Never leave the method without one of these:
Forgettingtry { // do the work ContextUtil.SetComplete(); } catch (Exception ex) { ContextUtil.SetAbort(); throw; }SetAbort()in the catch block is one of the most common causes of 0x8004E031 I've seen in 14 years of this stuff. - Check transaction flow across nested calls. If your method calls another serviced component that's marked
RequiresNeworNotSupported, the outer transaction context can end up in a weird state when the inner call returns. Set the inner component's transaction attribute toRequiredorSupportedif it should share the outer transaction. - For VB6 COM+ components, make sure every public method on the object calls
ObjectContext.SetCompleteorObjectContext.SetAbortbefore returning. TheMTSTransactionModeattribute doesn't do the voting for you. - Redeploy the component. After a fix, unregister the old COM+ application in Component Services, drop the DLL in place, and re-register. Stale component registrations are a real cause of weird COM+ behavior. Use
regsvcs.exefor .NET assemblies:regsvcs /appname:MyApp MyApp.dll
If it still fails after that
Check these in order. Don't skip ahead.
- COM+ application identity. Open Component Services, right-click the app, Properties > Identity. Make sure the account has rights to whatever the component touches — file shares, database, message queues.
- MSDTC is actually up. If the transaction spans two resources (SQL Server plus MSMQ, for example), DTC has to be running. Check the service and firewall rules. This is rare as a root cause for this error, but a misconfigured DTC can produce cascading COM+ failures that look similar.
- Event log entries at the same timestamp. The 0x8004E031 is often a secondary symptom. There's usually a primary error logged a few milliseconds earlier — a deadlock, a timeout, a file not found. Fix the primary and this clears.
- Transaction timeout. Default is 60 seconds. If your method runs long — a bulk import, a report — the transaction times out and COM+ aborts it, sometimes reporting 0x8004E031. Bump the timeout on the component's transaction property or break the work into chunks.
- Windows updates on the COM+ stack. There have been a handful of COM+ hotfixes over the years that touch transaction handling. If you're on a server that hasn't been patched in a year, get current before chasing this further. I've seen one case on Server 2012 R2 where a specific rollup fixed a phantom vote issue.
- Recompile and re-register. If nothing else works, wipe the component from Component Services, rebuild it clean, and re-register. Corrupted COM+ registration state is a real thing and it's not visible from the outside.
Ninety percent of the time, this is a missing vote call — [AutoComplete] in .NET or an explicit SetComplete/SetAbort in either stack. Fix the code first, then go hunting for infrastructure problems.