Fix 0X80010004: RPC_E_CANTCALLOUT_INASYNCCALL in COM+
This error means your code tried to make a synchronous call from inside an async COM call. The fix is to use CoWaitForMultipleHandles or restructure the call.
Quick Answer
For advanced users: The error 0X80010004 (RPC_E_CANTCALLOUT_INASYNCCALL) means your code is attempting a synchronous COM call from within an asynchronous COM call. To fix it, use CoWaitForMultipleHandles to wait without breaking the apartment rules, or restructure the async call to avoid the nested synchronous call entirely.
What's This Error Really About?
I remember the first time I hit 0X80010004 on a Windows Server 2019 box running a COM+ component. It was 2 AM, and the application log was full of those hex codes. The error text—RPC_E_CANTCALLOUT_INASYNCCALL—is the COM runtime telling you: "Hey, you're inside an async call, and you just tried to make a synchronous call back to the same apartment. That's not allowed."
Here's the specific trigger: You have a COM object that makes an asynchronous method call (like IAsyncRpcChannel or an async COM+ event). Inside that async call's callback, your code tries to call another COM method synchronously—perhaps to update a UI control, query another object, or log something. The COM apartment model (both STA and MTA) blocks this to prevent deadlocks. The runtime sees the call chain and returns 0X80010004 before the call even goes through.
This happens most often in server-side code that handles events from COM+ components, like a custom event sink that triggers a method call back into the originating object. Also common in ActiveX controls with async callbacks that try to talk back to the host.
Fix Steps: The Right Way
- Identify the call that's failing. Use a debugger (WinDbg) or enable COM traces via
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options. Look for the exact line that calls a COM method from within an async callback. For example, if you have a methodOnAsyncCompletethat callspObject->DoSomething(), that's the culprit. - Restructure the async callback. The cleanest fix: don't make synchronous calls from the callback at all. Instead, post a message or signal an event to a separate thread that handles the synchronous work. On Windows, use
PostMessagein an STA thread orSetEventfor an MTA worker thread. - If you must wait synchronously, use
CoWaitForMultipleHandlesinstead ofWaitForSingleObjectorSleep. This function pumps COM messages and allows the apartment to handle the async call properly. Here's the C++ signature:
Call it like this:HRESULT CoWaitForMultipleHandles(DWORD dwFlags, DWORD dwTimeout, ULONG cHandles, LPHANDLE pHandles, LPDWORD lpdwIndex);DWORD index; HRESULT hr = CoWaitForMultipleHandles(COWAIT_DISPATCH_CALLS | COWAIT_INPUTAVAILABLE, INFINITE, 1, &hEvent, &index); - Check your threading model. If the object is STA, the async callback might be coming from a different thread. Ensure you're marshaling the interface pointer correctly. Use
CoMarshalInterThreadInterfaceInStreamto pass the interface to the callback thread. - If using .NET interop with COM, set
[STAThread]on the callback method, or useControl.Invokein Windows Forms to marshal back to the UI thread. The error can also appear in PowerShell scripts that call COM objects asynchronously—use[System.Runtime.InteropServices.Marshal]::ReleaseComObjectcarefully.
Alternative Fixes When the Main One Fails
If restructuring the call isn't possible—say you're in a legacy codebase with no budget for refactoring—try these:
- Switch to an STA apartment for the async call. In some cases, marking the calling thread as STA (
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)) changes how the async call dispatches. This is a gamble but works for some single-threaded COM objects. - Use
CoAllowSetForegroundWindowif the call involves window handles. Rare, but I've seen it fix a variant of this error where the async callback tried to set focus. - Wrap the synchronous call in a
MessageBoxorSleep(0)loop withCoWaitForMultipleHandlesas a hack. Not pretty, but buys time until you can refactor. - Upgrade the COM+ component's threading model to "Both" or "Free" in the Component Services console. This lets the async call run on an MTA thread, which might avoid the restriction. Right-click the component, Properties, Concurrency tab.
Prevention Tip
Design your async callbacks to be pure notification handlers. They should not call back into the COM object that initiated the async call. Instead, queue the work elsewhere—a thread pool, a message loop, or even a simple list that a timer picks up. This pattern avoids 0X80010004 entirely and makes your code more testable. Also, always test async COM calls on a clean OS install (like Server 2022) with the exact threading model you'll deploy. I've seen this error pop up only on production machines with load.
Was this solution helpful?