Skip to content

fix(gateway): use unconditional KeepAlive in launchd plist - #10637

Closed
IISweetHeartII wants to merge 1 commit into
NousResearch:mainfrom
IISweetHeartII:fix/macos-launchd-keepalive
Closed

fix(gateway): use unconditional KeepAlive in launchd plist#10637
IISweetHeartII wants to merge 1 commit into
NousResearch:mainfrom
IISweetHeartII:fix/macos-launchd-keepalive

Conversation

@IISweetHeartII

Copy link
Copy Markdown

Summary

The generated launchd plist uses KeepAlive > SuccessfulExit = false, which means the gateway is not restarted when it exits with code 0 (clean exit). This causes the gateway to silently die and never recover in several real-world scenarios:

  • Discord slash command sync failure → gateway shuts down gracefully (exit 0) → launchd doesn't restart
  • --replace logic detects stale processes → exits cleanly → launchd doesn't restart
  • Any transient error handled with a graceful shutdown path

Before

<key>KeepAlive</key>
<dict>
    <key>SuccessfulExit</key>
    <false/>
</dict>

Gateway exits with code 0 → stays dead. Cron jobs stop firing. No recovery until manual restart.

After

<key>KeepAlive</key>
<true/>

<key>ThrottleInterval</key>
<integer>5</integer>

Gateway always restarts regardless of exit code. ThrottleInterval: 5 prevents overly aggressive restart loops.

Why this is safe

launchd_stop() already uses launchctl bootout which fully unloads the service definition, preventing KeepAlive from respawning. So intentional stops (via hermes gateway stop or hermes update) are completely unaffected.

Test plan

  • Gateway restarts automatically after exit 0 (previously stayed dead)
  • Gateway restarts after crash (non-zero exit) — unchanged behavior
  • hermes gateway stop / hermes update still cleanly stop the gateway
  • No restart loop — ThrottleInterval prevents rapid cycling
  • hermes cron status reports gateway as running after restart

Fixes #9659

The generated launchd plist uses `KeepAlive.SuccessfulExit = false`,
which only restarts the gateway on non-zero exit. When the gateway
exits cleanly (code 0) — e.g., after a transient Discord command
sync failure or a graceful shutdown triggered by `--replace` — launchd
does not restart it, leaving cron jobs unable to fire.

Changed to unconditional `KeepAlive: true` so the gateway always
restarts regardless of exit code. This matches the behavior users
expect from a daemon managed by launchd.

Added `ThrottleInterval: 5` to prevent overly aggressive restart
loops while still recovering quickly from crashes.

Intentional stops are unaffected: `launchd_stop()` already uses
`launchctl bootout` which fully unloads the service definition,
preventing KeepAlive from respawning the process.

Refs: #9659
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the contribution, @IISweetHeartII!

Closing this as a duplicate of #9689 (by @digging-airfare-4u), which targets the same fix/feature. We're consolidating on that PR for review.

If you want to help push it over the line, please jump in there — or if you think your approach is better for a specific reason that isn't covered in the other PR, let us know and we can reopen.

@teknium1 teknium1 closed this Apr 21, 2026
@IISweetHeartII
IISweetHeartII deleted the fix/macos-launchd-keepalive branch July 3, 2026 07:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

launchd plist KeepAlive should be unconditional, not SuccessfulExit-based

2 participants