Gateway service restart exit code and systemd retry fixes

NousResearch/hermes-agent · cluster 14051 · gateway

Data as of

Nothing open on GitHub. Fix merged in #18639. Check it covers your case.

The short answer

0open issues
0open PRs
5items grouped
Known?
Yes. 5 related items grouped here, 1 issue already closed.
Fix event
A fix was merged to main in PR #18639. Check it covers your case. No release data is tracked.
Tracker
The maintainers' tracker marks this unresolved. That is separate from the fix event.
Still open
Nothing open on GitHub.

These issues and PRs address how the gateway service signals restarts to systemd, including using a dedicated exit code to avoid exhausting StartLimitBurst and ensuring the systemd unit retries with appropriate backoff. Related changes also cover zombie PID detection during replacement and consistent use of the managed restart exit code on startup failure.

Open issues0

No open issues.

Open PRs0

No open pull requests.

Fixes on record

Merged to main. A stamp counts the issues a fix is linked to.

  • #18639 fix: gateway systemd unit now retries indefinitely with backoff

    merged to main

    files: hermes_cli/gateway.py

    Show the diff
    • hermes_cli/gateway.py
      @@ -188,7 +188,7 @@ def _graceful_restart_via_sigusr1(pid: int, drain_timeout: float) -> bool:
       
           SIGUSR1 is wired in gateway/run.py to ``request_restart(via_service=True)``
           which drains in-flight agent runs (up to ``agent.restart_drain_timeout``
      -    seconds), then exits with code 75.  Both systemd (``Restart=on-failure``
      +    seconds), then exits with code 75.  Both systemd (``Restart=always``
           + ``RestartForceExitStatus=75``) and launchd (``KeepAlive.SuccessfulExit
           = false``) relaunch the process after the graceful exit.
       
      @@ -1655,8 +1655,7 @@ def generate_systemd_unit(system: bool = False, run_as_user: str | None = None)
       Description={SERVICE_DESCRIPTION}
       After=network-online.target
       Wants=network-online.target
      -StartLimitIntervalSec=600
      -StartLimitBurst=5
      +StartLimitIntervalSec=0
       
       [Service]
       Type=simple
      @@ -1670,8 +1669,10 @@ def generate_systemd_unit(system: bool = False, run_as_user: str | None = None)
       Environment="PATH={sane_path}"
       Environment="VIRTUAL_ENV={venv_dir}"
       Environment="HERMES_HOME={hermes_home}"
      -Restart=on-failure
      -RestartSec=30
      +Restart=always
      +RestartSec=60
      +RestartMaxDelaySec=300
      +RestartSteps=5
       RestartForceExitStatus={GATEWAY_SERVICE_RESTART_EXIT_CODE}
       KillMode=mixed
       KillSignal=SIGTERM
      @@ -1691,9 +1692,9 @@ def generate_systemd_unit(system: bool = False, run_as_user: str | None = None)
           sane_path = ":".join(path_entries)
           return f"""[Unit]
       Description={SERVICE_DESCRIPTION}
      -After=network.target
      -StartLimitIntervalSec=600
      -StartLimitBurst=5
      +After=network-online.target
      +Wants=network-online.target
      +StartLimitIntervalSec=0
       
       [Service]
       Type=simple
      @@ -1702,8 +1703,10 @@ def generate_systemd_unit(system: bool = False, run_as_user: str | None = None)
       Environment="PATH={sane_path}"
       Environment="VIRTUAL_ENV={venv_dir}"
       Environment="HERMES_HOME={hermes_home}"
      -Restart=on-failure
      -RestartSec=30
      +Restart=always
      +RestartSec=60
      +RestartMaxDelaySec=300
      +RestartSteps=5
       RestartForceExitStatus={GATEWAY_SERVICE_RESTART_EXIT_CODE}
       KillMode=mixed
       KillSignal=SIGTERM
      @@ -2451,7 +2454,7 @@ def run_gateway(verbose: int = 0, quiet: bool = False, replace: bool = False):
           print()
           
           # Exit with code 1 if gateway fails to connect any platform,
      -    # so systemd Restart=on-failure will retry on transient errors
      +    # so systemd Restart=always will retry on transient errors
           verbosity = None if quiet else verbose
           try:
               success = asyncio.run(start_gateway(replace=replace, verbosity=verbosity))

3 PRs closed without merging

From the close comments: done elsewhere 2 · other 1

  • #14069 fix(gateway): exit with RestartForceExitStatus code and detect zombie PIDs in --replace · other → #14051, #13655
  • #14080 fix(gateway): use managed restart exit code on startup failure · done elsewhere → #14051, #18639
  • #14260 fix(gateway): use GATEWAY_SERVICE_RESTART_EXIT_CODE instead of hardcoded 1 · done elsewhere → #14051, #18639
Closed issues · 1
  • Closed#14051Gateway service: exit code mismatch causes StartLimitBurst=5 exhaustion on rapid restart
How this cluster was grouped
  • Link evidence (structural links throughout): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
  • Pairwise link density 0.40: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. 1.00 means every pair is linked.

Pick up a Lab task

Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.

Open the Lab

At a glance

Subsystem
gateway
Merged PRs
#18639
Closed issues
1
Attempts
3 PRs closed without merging
Fix link checked
2 May 2026

For your agent

The same facts, typed.