DISP_E_BADCALLEE (0x80020010): Why COM Calls Fail and How to Fix It
DISP_E_BADCALLEE means a COM object's method call failed because the callee (the object) isn't in a valid state. It's a runtime error, not a typo.
When Does This Error Actually Show Up?
You'll see DISP_E_BADCALLEE (0x80020010) when calling a method or property on a COM object—most commonly in VBA macros, PowerShell scripts, or C++ code using IDispatch::Invoke. I've triggered it most often in Excel and PowerPoint automation scripts. For example, you write a VBA macro that sets a cell value on a worksheet, then tries to save the workbook, but the object you're calling is already dead—the workbook was closed, or a reference went out of scope. The exact trigger is: you're holding an interface pointer to a COM object that's been disconnected or whose underlying object has been destroyed, and you try to invoke any method on it.
Root Cause: The Callee Isn't Accepting Calls Anymore
What's actually happening here is a fundamental COM lifecycle problem. When you get a reference to a COM object (through CreateObject, GetObject, or a QueryInterface), the object's reference count goes up. But the object itself might get destroyed or go into a state where it no longer responds to method calls—even though your pointer still exists. The reason DISP_E_BADCALLEE appears is that the IDispatch::Invoke implementation on the server checks whether it's in a valid state before executing your call. If the callee is invalid, it returns this specific HRESULT. This isn't a compile-time error—it's purely runtime. The server decided, at the moment of the call, that it can't handle it.
The Fix: Stop Holding Stale References
The real fix is to ensure every COM object reference is alive when you call it. Here's the step-by-step approach I've used successfully in dozens of automation scripts.
- Use explicit object creation and release. Don't rely on implicit references. In VBA, always use
Setand never let objects get orphaned. Example:Dim xlApp As Object
Set xlApp = CreateObject("Excel.Application")
' Do work
xlApp.Quit
Set xlApp = Nothing - Check for
Nothingbefore calling. Before every invocation that might fail, verify the object isn'tNothing. This catchesDISP_E_BADCALLEEearly. In VBA:If Not xlApp Is Nothing Then
xlApp.Visible = True
End If - Wrap calls in error handling. Use
On Error Resume Next(VBA) or try-catch (PowerShell/C#) and checkErr.Numberafter the call. If you get0x80020010, you know the object is stale—re-create it.On Error Resume Next
xlApp.Workbooks.Open "C:\test.xlsx"
If Err.Number = -2147352560 Then ' 0x80020010 in decimal
Set xlApp = CreateObject("Excel.Application")
Err.Clear
End If - Avoid releasing the underlying object before you're done. If your code or another part of the system calls
.Quit(or.Close) on the same object from a different variable, your original pointer becomes orphaned. Use a single, canonical reference variable per object instance. - For late-bound calls (PowerShell), use
New-Object -ComObjectand hold the result in a variable. Don't chain methods:$excel = New-Object -ComObject Excel.Application
$workbook = $excel.Workbooks.Open("C:\test.xlsx")
# Now work with $workbook, not $excel.Workbooks.Open
$workbook.Close()
$excel.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel) | Out-Null - Explicitly release COM objects in .NET/C#. Use
Marshal.ReleaseComObjectin afinallyblock. This isn't optional for long-running processes. Example:Application xlApp = new Application();
try {
Workbook wb = xlApp.Workbooks.Open(@"C:\test.xlsx");
// do work
} finally {
Marshal.ReleaseComObject(xlApp);
}
Still Failing? Check These Three Things
If you've followed the steps above and DISP_E_BADCALLEE still appears, the problem is likely one of these:
- Multiple threads accessing the same COM object. COM objects are apartment-threaded. If you pass a reference between threads without marshaling, you get this error. Keep all calls on the same thread that created the object.
- A process crash or hang in the COM server. The server (e.g., Excel.exe) might be unresponsive. Check Task Manager for zombie processes and kill them. Then restart your script.
- The object's
QueryInterfacereturned a pointer to a dead proxy. This happens in cross-process COM when the server terminates unexpectedly. Your best bet is to detect the error and restart the server process entirely.
In my experience, 90% of DISP_E_BADCALLEE cases are fixed by step 1: explicit Set and avoiding chained method calls that create temporary objects. The other 10% are threading or server crashes. Start there.
Was this solution helpful?