RPC_E_THREAD_NOT_INIT (0x8001010F): CoInitialize not called
This error means your thread skipped CoInitializeEx before making COM calls. It's common in C# async/await code where background threads miss the init step.
Quick answer
Call CoInitializeEx(ptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE) as the first thing in your thread. In C#, wrap the call in Thread.SetApartmentState(ApartmentState.STA) before starting it.
What's actually happening here
COM requires every thread that calls COM interfaces to initialize the COM library first. That's not optional. The CoInitializeEx call sets up the thread's apartment — either STA (single-threaded apartment) or MTA (multi-threaded apartment). Without it, COM rejects any call with RPC_E_THREAD_NOT_INIT (0x8001010F).
I see this most often in three specific scenarios:
- C# code using
async/awaitthat resumes on a thread-pool thread — those threads don't have COM initialized by default. - PowerShell scripts that call COM objects (like
New-Object -ComObject Word.Application) from a runspace or background job. - C++ code that creates a worker thread with
CreateThreadand forgets to callCoInitializeExat the top.
The fix isn't hard, but you have to do it on every thread that touches COM directly.
Fix steps
- Identify the offending thread. The stack trace will show the COM call that failed. Look for the thread that's calling into COM without init.
- In C++: At the very start of your thread function, call:
UseHRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); if (FAILED(hr)) { /* handle error */ } // ... your COM calls go here ... CoUninitialize();COINIT_MULTITHREADEDif you're sure your COM object supports MTA and you want better performance. But for Office interop and most UI-related COM, stick withCOINIT_APARTMENTTHREADED. - In C#: You can't call
CoInitializeExdirectly from managed code without P/Invoke. The cleaner way is to set the thread's apartment state:
If you're usingvar thread = new Thread(() => { // Your COM calls here }); thread.SetApartmentState(ApartmentState.STA); thread.Start();Task.Run, you can't change the apartment state of thread-pool threads. Instead, create a dedicated STA thread and marshal work to it. - In PowerShell: Use
-STAswitch when starting PowerShell, or wrap COM calls in a scriptblock executed on an STA thread:$sta = [System.Threading.Thread]::new({ param($script) [System.Threading.Thread]::CurrentThread.SetApartmentState('STA') & $script }) $sta.Start({ New-Object -ComObject Word.Application })
Alternative fixes if the main one fails
- Switch to MTA. If your COM object supports it, call
CoInitializeEx(NULL, COINIT_MULTITHREADED). This avoids the STA overhead but requires the object to be thread-safe. Most Office objects are not. - Marshal to an STA thread. If you can't change the thread's init (e.g., you're in a library), create a dedicated STA thread that hosts a hidden window and forward COM calls to it via
SendMessageorControl.Invokein .NET. - Use the COM object from the main thread. Often the simplest workaround — just don't use background threads for COM calls. But that kills concurrency.
Prevention tip
Always wrap your thread entry points with a guard. In C++, I use a RAII helper that calls CoInitializeEx in the constructor and CoUninitialize in the destructor. Then it's impossible to forget. For .NET, every thread that will call COM should have SetApartmentState called before Start. The pattern of forgetting this is so common that I now lint for it in CI pipelines — a regex check on new Thread without a matching SetApartmentState in the next 5 lines flags it.
Was this solution helpful?