Skip to content

WatchDog UIA focus listener crashes the whole MCP server after long uptime (COM "Catastrophic failure", no Python traceback) #332

Description

@Dmanleyjr

WatchDog UIA focus listener crashes the whole MCP server after long uptime (COM "Catastrophic failure", no Python traceback)

Summary

The local stdio server connects and runs fine, but after long uptime the UI-Automation WatchDog thread degrades: HandleFocusChangedEvent starts failing on every focus event with COMError (-2147220991, 'An event was unable to invoke any of the subscribers'), and eventually a single COMError (-2147418113, 'Catastrophic failure') (E_UNEXPECTED / 0x8000FFFF) takes down the entire process. Claude Desktop then shows the server as "Server disconnected."

This is not a startup, install, uv, or dependency problem. The server had connected cleanly ~17 hours earlier and answered initialize, tools/list, prompts/list, and resources/list. The crash is at runtime, in the watchdog.

Version / environment

  • Windows-MCP: 0.7.1 (installed as the Claude Desktop extension ant.dir.cursortouch.windows-mcp)
  • Host: Claude Desktop (MSIX/Store-packaged build), transport stdio, MODE=local
  • OS: Windows 11 Pro (build 26200)
  • Python: 3.13 (extension .venv)

Evidence (from Claude Desktop's mcp-server-Windows-MCP.log, paths redacted)

Clean connect the night before:

2026-07-12T23:05:46Z [Windows-MCP] Message from server: id=0 result   (initialize)
2026-07-12T23:05:46Z [Windows-MCP] method="notifications/initialized"
2026-07-12T23:05:46Z [Windows-MCP] tools/list / prompts/list / resources/list -> results
INFO:mcp.server.lowlevel.server:Processing request of type ListToolsRequest

Then ~17 hours of this, thousands of times:

DEBUG:windows_mcp.watchdog.event_handlers:Focus callback COM error: (-2147220991, 'An event was unable to invoke any of the subscribers', (None, None, None, 0, None))
...
DEBUG:windows_mcp.watchdog.event_handlers:Focus callback COM error: (-2147418113, 'Catastrophic failure', (None, None, None, 0, None))

Immediately followed by process death:

2026-07-13T16:29:29Z [Windows-MCP] Server transport closed unexpectedly, this is likely due to the process exiting early.
2026-07-13T16:29:29Z [Windows-MCP] [error] Server disconnected.

Total uptime before crash: ~17h24m.

Root cause analysis

  • WatchDog._run (src/windows_mcp/watchdog/service.py) drives an STA event loop and calls comtypes.client.PumpEvents(0.1) each iteration.
  • FocusChangedEventHandler.HandleFocusChangedEvent (src/windows_mcp/watchdog/event_handlers.py:19) catches comtypes.COMError and logs it, so the per-event -2147220991 failures are non-fatal and simply spam the log. The UIA event subscription is never re-established, so once degraded it stays degraded.
  • The final -2147418113 (E_UNEXPECTED) is not surfaced as a catchable Python exception: the outer except Exception: logger.error("WatchDogService died") in _run was never logged. The process just exits. This is consistent with a native COM callback invoked against a torn-down/dangling interface during PumpEvents (an access violation), which crashes the interpreter rather than raising.
  • Because the watchdog runs in the same process as the FastMCP stdio server, its native crash takes the server down with it, producing the "Server disconnected" state.

Likely trigger for the pipeline degradation: a desktop/session state change during the long uptime (sleep/resume, lock/unlock, display or GPU reconfiguration). The subscription made via AddFocusChangedEventHandler does not survive that, and there is no recovery path.

Impact

  • The single most useful long-running tool (desktop automation) silently dies after the machine has been up a while, and the user only discovers it when a call fails or they open Settings.
  • There is currently no supported way to disable the watchdog: WatchDog() is constructed unconditionally in src/windows_mcp/__main__.py:56, with no env var or manifest.json user_config toggle. So there is no user-side workaround short of restarting the extension.

Suggested fixes (in rough priority order)

  1. Isolate the watchdog crash from the server. A watchdog failure should never terminate the MCP server. The watchdog is an optimization (focus-tree cache); on failure, fall back to on-demand tree queries and keep serving tools.
  2. Recover the subscription instead of spiraling. Count consecutive COMErrors in HandleFocusChangedEvent; past a threshold, RemoveFocusChangedEventHandler + re-Add... (ideally after re-creating the IUIAutomation client, since the existing pointer may be corrupt). This turns a permanent degradation into a self-healing reconnect.
  3. Add a disable switch. A WINDOWS_MCP_WATCHDOG=off env var (wired into manifest.json user_config like the existing screenshot_backend/debug toggles) would give users on unstable UIA environments a supported opt-out.
  4. Re-initialize COM/UIA on E_UNEXPECTED. Treat -2147418113 specifically: tear the STA thread down cleanly, re-CoInitialize, rebuild the client, and restart, rather than reusing state that just reported catastrophic failure.
  5. Make degradation observable before the crash. Rate-limit the per-event COMError log (it currently emits thousands of identical lines) and emit a single WARN plus a heartbeat when the focus pipeline has been failing for N seconds.

Repro notes

Not deterministic on demand; it correlates with long uptime plus a session/display state change. Leaving local mode running overnight on a machine that sleeps/resumes reproduces the -2147220991 flood; the terminal -2147418113 and process exit follow once the COM state fully collapses.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions