From a4d474777fa149d3ccd543e8cda7ffa5d3fdfff3 Mon Sep 17 00:00:00 2001 From: teknium1 <127238744+teknium1@users.noreply.github.com> Date: Mon, 14 Sep 2026 16:58:35 -0700 Subject: [PATCH] fix(computer-use): doctor diagnoses a denied cua-driver spawn instead of crashing On Windows the Hermes venv interpreter cannot CreateProcess a binary under C:\Program Files\WindowsApps (WinError 5) even though the shell resolves it, so `hermes computer-use doctor` died with a raw PermissionError traceback from _open_mcp. Catch the spawn OSError and print what failed, why the tool may still work (PATH resolves another copy), and the fix (reinstall outside WindowsApps or HERMES_CUA_DRIVER_CMD), exit 2. --- tests/computer_use/test_doctor.py | 11 +++++++++++ tools/computer_use/doctor.py | 9 +++++++++ 2 files changed, 20 insertions(+) diff --git a/tests/computer_use/test_doctor.py b/tests/computer_use/test_doctor.py index ad7c699c50..89b524fd8f 100644 --- a/tests/computer_use/test_doctor.py +++ b/tests/computer_use/test_doctor.py @@ -144,6 +144,17 @@ class TestDoctorExitCodes: code = doctor.run_doctor() assert code == 1 + def test_spawn_denied_exits_2_with_diagnosis(self, capsys): + """The runtime interpreter cannot execute the resolved binary (Windows WinError 5 on a + `WindowsApps` install): a diagnosis + exit 2, never a raw traceback.""" + from tools.computer_use import doctor + + with patch("shutil.which", return_value="/protected/cua-driver"), \ + patch("subprocess.Popen", side_effect=PermissionError(13, "Access is denied")): + code = doctor.run_doctor() + assert code == 2 + err = capsys.readouterr().err + assert "Access is denied" in err and "HERMES_CUA_DRIVER_CMD" in err def test_protocol_error_exits_2(self, capsys): """An empty stdout response (driver crashed during handshake) is a diff --git a/tools/computer_use/doctor.py b/tools/computer_use/doctor.py index 87470dd15a..6c2fa7ddcc 100644 --- a/tools/computer_use/doctor.py +++ b/tools/computer_use/doctor.py @@ -339,6 +339,15 @@ def run_doctor(driver_cmd: Optional[str] = None, *, include: Sequence[str] = (), report = _drive_health_report(binary, include=include, skip=skip, timeout=12.0) except HealthReportUnavailable as e: report = _compose_fallback_report(binary, reason=str(e), timeout=12.0) + except OSError as e: + # The spawn itself failed (Windows: a venv interpreter denied `CreateProcess` on a binary under + # `C:\Program Files\WindowsApps`, WinError 5). A traceback here hides the one fact the user needs. + print(f"cua-driver could not be started from {binary!r}: {e}\n" + " The Hermes runtime interpreter cannot execute this binary; the tool may still work because the\n" + " shell resolves a different copy on PATH. Fix: install cua-driver outside the protected directory\n" + " (e.g. the upstream installer's default under your user profile) or point HERMES_CUA_DRIVER_CMD at\n" + " a copy the runtime can execute, then re-run `hermes computer-use doctor`.", file=sys.stderr) + return 2 except RuntimeError as e: print(f"cua-driver health_report failed: {e}", file=sys.stderr) return 2