Skip to content

[Bug]: hermes gateway start/install fails on macOS with launchctl bootstrap exit status 5 (Input/output error) #11323

@ajason

Description

@ajason

Bug Description

On macOS, hermes gateway start and hermes gateway install can fail with a CalledProcessError when launchctl bootstrap returns exit status 5 (Input/output error). This leaves the launchd service unloaded and the Telegram/Discord/WhatsApp gateway unreachable.

The root cause appears to be that launchctl bootstrap does not reliably load user LaunchAgents on newer macOS versions, especially when the job domain state is slightly out of sync. In this state, launchctl load -w succeeds immediately for the same plist file.

Environment

  • OS: macOS 15.4.1 (Darwin 24.4.0, Apple Silicon)
  • Python: 3.11.15
  • Hermes: v0.10.0 (2026.4.16), main @ 764536b6

Steps to Reproduce

  1. Ensure you are on macOS with a user LaunchAgent setup.
  2. Run hermes gateway install or hermes gateway start.
  3. If launchctl bootstrap encounters an I/O error (exit 5), the command crashes:
Installing launchd service to: /Users/<user>/Library/LaunchAgents/ai.hermes.gateway.plist
Traceback (most recent call last):
  ...
  File "hermes_cli/gateway.py", line 1426, in launchd_install
    subprocess.run(["launchctl", "bootstrap", _launchd_domain(), str(plist_path)], check=True, timeout=30)
subprocess.CalledProcessError: Command '['launchctl', 'bootstrap', 'gui/501', '/Users/.../ai.hermes.gateway.plist']' returned non-zero exit status 5.

Expected Behavior

Gateway service installation and start should be resilient to launchctl bootstrap transient failures. If bootstrap fails, Hermes should fall back to launchctl load -w <plist>, which is the traditional and more compatible way to load a user launch agent on macOS.

Actual Behavior

The CLI exits with an unhandled exception and the gateway remains down.

Proposed Fix

Wrap the three launchctl bootstrap calls in hermes_cli/gateway.py (launchd_install, launchd_start missing-plist path, and launchd_start unloaded-job path) with a try/except that falls back to:

subprocess.run(["launchctl", "load", "-w", str(plist_path)], check=True, timeout=30)

This has been verified locally to resolve the issue.

Related Issues / PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    comp/cliCLI entry point, hermes_cli/, setup wizardcomp/gatewayGateway runner, session dispatch, deliverytype/bugSomething isn't working

    Type

    No type
    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