wailsapp/wails · error

no focused element to type into; pass a selector or click a

Error message

no focused element to type into; pass a selector or click a field first

What it means

CoInitializeEx initializes COM on a thread; E_UNEXPECTED indicates a generic, unforeseeable failure inside the COM runtime for that thread. The w32 wrapper panics with 'CoInitializeEx failed with E_UNEXPECTED'. Unlike the argument and memory cases, this HRESULT from CoInitializeEx usually means the thread's COM state is corrupted — e.g. mixed runtime versions, DLLs unloaded out of order, or a prior CoUninitialize imbalance on the same thread.

Source

Thrown at v3/pkg/application/mcp_inject.js:637

    function shiftFocus(forward) {
        const focusables = Array.from(document.querySelectorAll(
            'a[href], button, input, textarea, select, [tabindex]:not([tabindex="-1"])',
        )).filter((el) => !el.disabled && el.offsetParent !== null);
        if (focusables.length === 0) return;
        const index = focusables.indexOf(document.activeElement);
        const next = forward
            ? focusables[(index + 1) % focusables.length]
            : focusables[(index - 1 + focusables.length) % focusables.length];
        next.focus();
    }

    async function typeText(text, selector, delay) {
        if (selector) {
            await click({ selector: selector });
        }
        let el = document.activeElement;
        if (!el || el === document.body) {
            throw new Error('no focused element to type into; pass a selector or click a field first');
        }
        const pause = typeof delay === 'number' && delay >= 0 ? delay : 25;
        for (const ch of text) {
            const key = ch === '\n' ? 'Enter' : ch;
            await pressKey(el, key, {});
            if (pause > 0) await sleep(pause);
            el = document.activeElement || el;
        }
        const editable = editableHost(el);
        if (editable) editable.dispatchEvent(new Event('change', { bubbles: true }));
        return describe(el);
    }

    async function press(key, modifiers) {
        const el = document.activeElement || document.body;
        await pressKey(el, key, modifierInit(modifiers));
        return describe(document.activeElement || el);
    }

View on GitHub (pinned to 0e754b1b40)

Solutions

  1. Audit CoInitializeEx/CoUninitialize pairing per thread: every successful init (including S_FALSE returns) needs exactly one CoUninitialize.
  2. Do not FreeLibrary DLLs that registered COM objects on threads that still use COM.
  3. Recreate the thread (goroutine on a fresh OS thread with LockOSThread) rather than re-initializing COM on a corrupted one.
  4. Move COM usage onto one dedicated, long-lived thread that initializes once at start.

Example fix

// before
// cleanup path somewhere: CoUninitialize() called twice
CoInitializeEx(COINIT_APARTMENTTHREADED) // later init -> E_UNEXPECTED

// after
// ensure strict pairing: init once per thread, uninit once
runtime.LockOSThread()
defer runtime.UnlockOSThread()
hr := CoInitializeEx(COINIT_APARTMENTTHREADED)
defer CoUninitialize()
Defensive patterns

Strategy: try-catch

Try / catch

defer func() {
	if r := recover(); r != nil {
		if msg, _ := r.(string); strings.Contains(msg, "E_UNEXPECTED") {
			err = fmt.Errorf("com thread state corrupted; recreate thread")
			return
		}
		panic(r)
	}
}()
hr := CoInitializeEx(flags)

Prevention

When it happens

Trigger: Calling CoInitializeEx on a thread where an earlier CoInitialize/CoUninitialize sequence was unbalanced (extra CoUninitialize dropped the thread below a clean state); unloading a DLL (FreeLibrary) that had registered COM class objects on that thread; nested COM usage from a plugin loaded and unloaded repeatedly.

Common situations: Plugin systems that LoadLibrary COM-registering DLLs and free them while the host thread still uses COM; long-lived threads that re-initialize COM after teardown with an unmatched CoUninitialize somewhere in cleanup.

Related errors


AI-assisted analysis of wailsapp/wails@0e754b1b40 (2026-08-15). Data as JSON: /api/errors/d6ad997eb349ee0e. Report an issue: GitHub.