dotnet/wpf · error · ElementNotAvailableException

ERROR_INVALID_MENU_HANDLE..ERROR_WINDOW_OF_OTHER_THREAD (1401-1408)

ERROR_INVALID_MENU_HANDLE..ERROR_WINDOW_OF_OTHER_THREAD (1401-1408)

Error message

ElementNotAvailableException

What it means

Misc's Win32 error-code mapper throws ElementNotAvailableException for system error codes 1400-1408, including ERROR_INVALID_MENU_HANDLE (1401) through ERROR_WINDOW_OF_OTHER_THREAD (1408). These codes indicate the window/menu/hook resource the call targeted is invalid or belongs to another thread, so the corresponding UIA element is treated as no longer available.

Solutions

  1. Re-acquire the element/handle — the underlying Win32 resource has gone stale
  2. Ensure UIA calls to another app's windows go through the proper cross-process automation path, not raw handles
  3. Catch ElementNotAvailableException around pattern/property calls and re-walk the tree
  4. Check that menus/popups are still open before querying their handles

Example fix

// before
var className = Misc.RealGetWindowClass(hwnd); // throws on 1401-1408
// after
try
{
    var className = Misc.RealGetWindowClass(hwnd);
}
catch (ElementNotAvailableException)
{
    // invalid menu/window handle or other-thread window: re-acquire element
    hwnd = RefreshHandle();
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Invalidate stale handles before use:
if (!IsWindow(hwnd) || !IsMenu(hMenu)) { ReacquireHandles(); }

Try / catch

try { QueryWindow(hwnd); } catch (ElementNotAvailableException) { ReacquireHandles(); Retry(); }

Prevention

When it happens

Trigger: A Win32 call made by the proxy fails with GetLastError in the 1400-1408 range — invalid window handles, invalid menu handles (1401), or a window owned by another thread (1408), typically during RealGetWindowClass, menu queries, or cross-thread window operations.

Common situations: Menu handles going stale after a menu closed; cross-thread access to windows of a UI thread that is busy or gone; x64-specific behavior seen with RealGetWindowClass.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/c312dc2987b9b2a2. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/UIAutomation/UIAutomationClientSideProviders/MS/Internal/AutomationProxies/Misc.cs:1682

        internal static void ThrowWin32ExceptionsIfError(int errorCode)
        {
            switch (errorCode)
            {
                case 0:     //    0 ERROR_SUCCESS                   The operation completed successfully.
                    // The error code indicates that there is no error, so do not throw an exception.
                    break;

                case 6:     //    6 ERROR_INVALID_HANDLE            The handle is invalid.
                case 1400:  // 1400 ERROR_INVALID_WINDOW_HANDLE     Invalid window handle.
                case 1401:  // 1401 ERROR_INVALID_MENU_HANDLE       Invalid menu handle.
                case 1402:  // 1402 ERROR_INVALID_CURSOR_HANDLE     Invalid cursor handle.
                case 1403:  // 1403 ERROR_INVALID_ACCEL_HANDLE      Invalid accelerator table handle.
                case 1404:  // 1404 ERROR_INVALID_HOOK_HANDLE       Invalid hook handle.
                case 1405:  // 1405 ERROR_INVALID_DWP_HANDLE        Invalid handle to a multiple-window position structure.
                case 1406:  // 1406 ERROR_TLW_WITH_WSCHILD          Cannot create a top-level child window.
                case 1407:  // 1407 ERROR_CANNOT_FIND_WND_CLASS     Cannot find window class.
                case 1408:  // 1408 ERROR_WINDOW_OF_OTHER_THREAD    Invalid window; it belongs to other thread.
                    throw new ElementNotAvailableException();

                // We're getting this in AMD64 when calling RealGetWindowClass; adding this code
                // to allow the DRTs to pass while we continue investigation.
                case 87:    //   87 ERROR_INVALID_PARAMETER
                    throw new ElementNotAvailableException();

                case 8:     //    8 ERROR_NOT_ENOUGH_MEMORY         Not enough storage is available to process this command.
                case 14:    //   14 ERROR_OUTOFMEMORY               Not enough storage is available to complete this operation.
                    throw new OutOfMemoryException();

                case 998:   //  998 ERROR_NOACCESS                  Invalid access to memory location.
                case 5:     //    5 ERROR_ACCESS_DENIED
                    throw new InvalidOperationException();

                case 121:   //  121 ERROR_SEM_TIMEOUT               The semaphore timeout period has expired.
                case 258:   //  258 WAIT_TIMEOUT                    The wait operation timed out.
                case 1053:  // 1053 ERROR_SERVICE_REQUEST_TIMEOUT   The service did not respond to the start or control request in a timely fashion.
                case 1460:  // 1460 ERROR_TIMEOUT                   This operation returned because the timeout period expired.

View on GitHub (pinned to 81131a70a4)