0X80020010

DISP_E_BADCALLEE (0x80020010): Why COM Calls Fail and How to Fix It

Windows Errors Intermediate 👁 1 views 📅 May 28, 2026

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.

  1. Use explicit object creation and release. Don't rely on implicit references. In VBA, always use Set and never let objects get orphaned. Example:
    Dim xlApp As Object
    Set xlApp = CreateObject("Excel.Application")
    ' Do work
    xlApp.Quit
    Set xlApp = Nothing
  2. Check for Nothing before calling. Before every invocation that might fail, verify the object isn't Nothing. This catches DISP_E_BADCALLEE early. In VBA:
    If Not xlApp Is Nothing Then
    xlApp.Visible = True
    End If
  3. Wrap calls in error handling. Use On Error Resume Next (VBA) or try-catch (PowerShell/C#) and check Err.Number after the call. If you get 0x80020010, 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
  4. 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.
  5. For late-bound calls (PowerShell), use New-Object -ComObject and 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
  6. Explicitly release COM objects in .NET/C#. Use Marshal.ReleaseComObject in a finally block. 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 QueryInterface returned 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?