Conversation
…lback in gateway status
## Description
On macOS 26.x, `launchctl bootstrap` and `launchctl kickstart` return exit code 5 ("Input/output error"), which Hermes already anticipates and handles by spawning a detached fallback process. However, the gateway status reporting is ambiguous:
- `gateway status` says "Gateway service is loaded" (because `launchctl list` returns exit 0)
- But `launchctl print` shows `state = not running` — launchd isn't actually supervising anything
- The detached fallback PID running is invisible to the status command
- Users can't tell whether auto-start at login and auto-restart on crash are available
### Root Cause
Two problems in `hermes_cli/gateway.py`:
1. **`_probe_launchd_service_running()`** (line 1067): Determined launchd service liveness solely by `launchctl list <label>` exit code. On macOS 26, this returns 0 even when the service is only *registered* but not running (output lacks a `"PID"` field). This caused `GatewayRuntimeSnapshot.service_running = True` incorrectly, which suppressed the process/service mismatch warning.
2. **`launchd_status()`** (line 3569): Used the same binary "loaded/not loaded" check without inspecting whether launchd actually has a PID, whether a detached fallback is running, or whether auto-start/restart are available.
### Changes
**`hermes_cli/gateway.py`:**
1. **New `_parse_launchd_pid_from_list_output()` helper** — Extracts the PID from `launchctl list` output. When launchd is actively supervising, the output includes `"PID" = <number>;`. When only registered but not running, no PID field is present.
2. **Fixed `_probe_launchd_service_running()`** — Now requires a PID in the `launchctl list` output to confirm launchd is actually supervising. This correctly sets `service_running = False` when launchd has the service registered but `state = not running`, which triggers the existing process/service mismatch detection.
3. **Reworked `launchd_status()`** — Reports clearly separated information:
- LaunchAgent plist currentness (stale or current)
- Whether launchd is actively supervising (with PID)
- Whether a detached fallback PID is running
- Whether auto-start at login and auto-restart on crash are available
- When launchd supervision is known to be unavailable, explains why
4. **Persistent unsupported marker** (`~/.hermes/.gateway-launchd-unsupported`) — Written when `_launchd_fallback_to_detached()` is called (launchd exit 5/125). Allows `launchd_status()` to explain *why* launchd can't supervise even when no fallback process is currently running. Cleared automatically when a future bootstrap/kickstart succeeds (e.g., after an OS update fixes the issue).
5. **Updated `_print_gateway_process_mismatch()`** — Distinguishes the managed detached fallback from a genuinely manual `nohup hermes gateway run`, providing accurate guidance for each case.
### Status Output Examples
**Before** (macOS 26, fallback active):
```
Launchd plist: ~/Library/LaunchAgents/ai.hermes.gateway.plist
✓ Service definition matches the current Hermes install
✓ Gateway service is loaded
{
"Label" = "ai.hermes.gateway";
"OnDemand" = true;
...
};
```
**After** (macOS 26, fallback active):
```
Launchd plist: ~/Library/LaunchAgents/ai.hermes.gateway.plist
✓ Service definition matches the current Hermes install
⚠ Gateway service is registered but launchd is not supervising it
launchd cannot manage the gateway on this macOS version.
✓ Detached fallback process is running (PID 12345)
Cron jobs will fire. Stop with: hermes gateway stop
⚠ Auto-start at login and auto-restart on crash are NOT available.
```
**After** (normal launchd supervision):
```
Launchd plist: ~/Library/LaunchAgents/ai.hermes.gateway.plist
✓ Service definition matches the current Hermes install
✓ Gateway is supervised by launchd (PID 12345)
Auto-start at login and auto-restart on crash are available.
```
### Tests
Updated 5 existing tests and added 11 new tests in `tests/hermes_cli/test_gateway_service.py`:
- PID parsing from `launchctl list` output (with PID, without PID, empty, unquoted PID)
- `_probe_launchd_service_running()` requires PID presence
- Unsupport marker lifecycle (write, clear, persist across fallback)
- Marker cleared on successful bootstrap
- `launchd_status()` reporting: supervised, fallback-running, fallback-unavailable
- Existing fallback tests now verify marker creation
### Related Issues
- Issue NousResearch#23387 (original macOS 26 launchd workaround)
- Issue NousResearch#42524 (this issue)
tonydwb
approved these changes
Jun 9, 2026
tonydwb
left a comment
There was a problem hiding this comment.
Code Review Summary
Verdict: Approved
Overview
This PR improves macOS launchd supervision detection by distinguishing between launchd actively supervising a process vs. a registered-but-not-running service (macOS 26+ behavior).
Changes
- Adds
_parse_launchd_pid_from_list_output()to extract PID from launchctl output - Updates
_probe_launchd_service_running()to require a PID for active supervision - Adds unsupported marker persistence for launchd domain issues
- Updates status reporting to explain when launchd cannot supervise
- Extensive test coverage for all scenarios
Quality
- Clean, well-scoped fix
- Good documentation
- No security concerns
- Excellent test coverage
Reviewed by Hermes Agent
This was referenced Jun 26, 2026
Contributor
|
Salvaged into #52984 (rebased onto current main, 506 commits ahead — cherry-pick applied cleanly, 3 fixes applied for main drift: missing json/datetime imports, case-sensitivity in test assertions, temp-home guard mock). All credit to @Dr1985 for the original fix. Thanks for the contribution! Note: PR #52983 (AUTHOR_MAP chore) must merge first. |
kshitijk4poor
added a commit
that referenced
this pull request
Jun 26, 2026
pai-scaffolde
pushed a commit
to Scaffolde/hermes-agent
that referenced
this pull request
Jun 28, 2026
waefrebeorn
pushed a commit
to waefrebeorn/slermes
that referenced
this pull request
Jul 2, 2026
habarmc1223-sudo
pushed a commit
to habarmc1223-sudo/hermes-agent-fluxmem
that referenced
this pull request
Jul 8, 2026
santhreal
pushed a commit
to santhreal/hermes-agent
that referenced
this pull request
Jul 13, 2026
Gravezzz
pushed a commit
to Gravezzz/hermes-agent
that referenced
this pull request
Jul 21, 2026
leewenjie
pushed a commit
to leewenjie/hermes-agent
that referenced
this pull request
Aug 7, 2026
melon-xf
added a commit
to melon-xf/hermes-agent
that referenced
this pull request
Sep 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
…lback in gateway status
Description
On macOS 26.x,
launchctl bootstrapandlaunchctl kickstartreturn exit code 5 ("Input/output error"), which Hermes already anticipates and handles by spawning a detached fallback process. However, the gateway status reporting is ambiguous:gateway statussays "Gateway service is loaded" (becauselaunchctl listreturns exit 0)launchctl printshowsstate = not running— launchd isn't actually supervising anythingRoot Cause
Two problems in
hermes_cli/gateway.py:_probe_launchd_service_running()(line 1067): Determined launchd service liveness solely bylaunchctl list <label>exit code. On macOS 26, this returns 0 even when the service is only registered but not running (output lacks a"PID"field). This causedGatewayRuntimeSnapshot.service_running = Trueincorrectly, which suppressed the process/service mismatch warning.launchd_status()(line 3569): Used the same binary "loaded/not loaded" check without inspecting whether launchd actually has a PID, whether a detached fallback is running, or whether auto-start/restart are available.Changes
hermes_cli/gateway.py:New
_parse_launchd_pid_from_list_output()helper — Extracts the PID fromlaunchctl listoutput. When launchd is actively supervising, the output includes"PID" = <number>;. When only registered but not running, no PID field is present.Fixed
_probe_launchd_service_running()— Now requires a PID in thelaunchctl listoutput to confirm launchd is actually supervising. This correctly setsservice_running = Falsewhen launchd has the service registered butstate = not running, which triggers the existing process/service mismatch detection.Reworked
launchd_status()— Reports clearly separated information:Persistent unsupported marker (
~/.hermes/.gateway-launchd-unsupported) — Written when_launchd_fallback_to_detached()is called (launchd exit 5/125). Allowslaunchd_status()to explain why launchd can't supervise even when no fallback process is currently running. Cleared automatically when a future bootstrap/kickstart succeeds (e.g., after an OS update fixes the issue).Updated
_print_gateway_process_mismatch()— Distinguishes the managed detached fallback from a genuinely manualnohup hermes gateway run, providing accurate guidance for each case.Status Output Examples
Before (macOS 26, fallback active):
After (macOS 26, fallback active):
After (normal launchd supervision):
Tests
Updated 5 existing tests and added 11 new tests in
tests/hermes_cli/test_gateway_service.py:launchctl listoutput (with PID, without PID, empty, unquoted PID)_probe_launchd_service_running()requires PID presencelaunchd_status()reporting: supervised, fallback-running, fallback-unavailableRelated Issues
What does this PR do?
This PR fixes ambiguous gateway status reporting on macOS 26.x. When launchd cannot supervise the gateway (exit code 5), Hermes falls back to a detached background process, but hermes gateway status previously reported "Gateway service is loaded" without mentioning that launchd isn't actually supervising, that a fallback PID is running, or that auto-start/restart are unavailable. The fix adds PID-aware launchd probing, a persistent unsupported marker, and clear status output distinguishing launchd supervision from detached fallback.
Related Issue
Fixes #42524
Fixes #
Type of Change
Changes Made
hermes_cli/gateway.py:_parse_launchd_pid_from_list_output()to extract PID fromlaunchctl listoutput_probe_launchd_service_running()to require PID presence, not just exit code 0launchd_status()with four clear output paths (supervised / fallback-running / fallback-unavailable / not-loaded)~/.hermes/.gateway-launchd-unsupported) with write/clear/exists helpers_print_gateway_process_mismatch()to distinguish fallback from manual run_launchd_fallback_to_detached(), cleared on 5 bootstrap success pathstests/hermes_cli/test_gateway_service.py:How to Test
hermes gateway start— observe fallback message and marker created at~/.hermes/.gateway-launchd-unsupportedhermes gateway status— verify output clearly shows:hermes gateway stop— verify fallback PID is cleaned uphermes gateway statusagain — verify it shows "No fallback process is running" with clear guidancegateway statusshows "Gateway is supervised by launchd (PID X)"scripts/run_tests.sh tests/hermes_cli/test_gateway_service.py::TestLaunchdServiceRecovery -vChecklist
Code
I've read the Contributing Guide
My commit messages follow Conventional Commits (
fix(scope):,feat(scope):, etc.)I searched for existing PRs to make sure this isn't a duplicate
My PR contains only changes related to this fix/feature (no unrelated commits)
I've run
pytest tests/ -qand all tests passI've added tests for my changes (required for bug fixes, strongly encouraged for features)
I've tested on my platform: macOS 26.x
Documentation & Housekeeping
is_macos().No Windows/Linux impact. New functions are only called from macOS code paths.
For New Skills
hermes --toolsets skills -q "Use the X skill to do Y"Screenshots / Logs