# NousResearch/hermes-agent · Gateway service restart exit code and systemd retry fixes

## Open now

- 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.

## TL;DR

- 5 member(s): 0 open issue(s), 1 closed issue(s), 0 open PR(s), 1 merged PR(s), 3 PR(s) closed without merging.
- A fix was merged to main in PR #18639. Verify it addresses your case.
- **Recommended:** read PR #18639 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `unresolved`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_SPANNING`): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
- Pairwise link density `0.40`: the share of member 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.

## Closed / duplicate issues

- #14051: Gateway service: exit code mismatch causes StartLimitBurst=5 exhaustion on rapid restart

## Prior & competing PRs

- **#14069: fix(gateway): exit with RestartForceExitStatus code and detect zombie PIDs in --replace**. closed without merge · fix tier `suggested`
  - maintainer direction: category `uncategorized`; implication `needs_llm`; refs `#14051`, `#13655`, `#13655`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#18639: fix: gateway systemd unit now retries indefinitely with backoff**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `hermes_cli/gateway.py`
  - fix hunks:
  - `hermes_cli/gateway.py`

    ```diff
    @@ -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.
     
    ```

    ```diff
    @@ -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
    ```

    ```diff
    @@ -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
    ```

    ```diff
    @@ -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
    ```

    ```diff
    @@ -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
    ```

    ```diff
    @@ -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))
    ```
- **#14080: fix(gateway): use managed restart exit code on startup failure**. closed without merge
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `#14051`, `#18639`
- **#14260: fix(gateway): use GATEWAY_SERVICE_RESTART_EXIT_CODE instead of hardcoded 1**. closed without merge
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `#14051`, `#18639`

## Fix status

- Merged to main in PR #18639 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-05-02`.
- Subsystem: `gateway`
