NousResearch/hermes-agent

Gateway --system CLI reads wrong HERMES_HOME

Cluster 22035. Data as of

Nothing open on GitHub. Fix merged in #22803 on 9 May 2026. Check it covers your case.

The short answer

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

A bug where gateway --system operations (e.g. restart) incorrectly read status from root's HERMES_HOME instead of the unit's, causing false failure reports. The members include the bug report and related fix attempts.

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.

  • #22803 fix(gateway): adopt unit's HERMES_HOME for --system CLI ops (salvage #22533)

    merged to main

    files: hermes_cli/gateway.py, scripts/release.py

    Show the diff
    • hermes_cli/gateway.py
      @@ -635,6 +635,66 @@ def _probe_systemd_service_running(system: bool = False) -> tuple[bool, bool]:
           return selected_system, result.stdout.strip() == "active"
       
       
      +def _read_systemd_unit_environment(system: bool = False) -> dict[str, str]:
      +    """Parse the gateway unit's ``Environment=`` directives.
      +
      +    ``systemctl show -p Environment`` returns a single line of
      +    space-separated ``KEY=VALUE`` pairs; values are not quoted in the output
      +    even when the unit file quoted them. We split on whitespace and ``=``.
      +    """
      +    selected_system = _select_systemd_scope(system)
      +    try:
      +        result = _run_systemctl(
      +            [
      +                "show",
      +                get_service_name(),
      +                "--no-pager",
      +                "--property",
      +                "Environment",
      +            ],
      +            system=selected_system,
      +            capture_output=True,
      +            text=True,
      +            timeout=10,
      +        )
      +    except (RuntimeError, subprocess.TimeoutExpired, OSError):
      +        return {}
      +    if result.returncode != 0:
      +        return {}
      +    parsed: dict[str, str] = {}
      +    for line in result.stdout.splitlines():
      +        if not line.startswith("Environment="):
      +            continue
      +        body = line[len("Environment="):].strip()
      +        for token in body.split():
      +            if "=" not in token:
      +                continue
      +            key, value = token.split("=", 1)
      +            parsed[key] = value
      +    return parsed
      +
      +
      +def _sync_hermes_home_from_systemd_unit(system: bool) -> None:
      +    """When acting on a system-scope unit, adopt its ``HERMES_HOME``.
      +
      +    Under ``sudo``, ``HERMES_HOME`` is stripped and ``HOME=/root``, so
      +    :func:`get_hermes_home` falls back to ``/root/.hermes`` — the wrong
      +    profile. The unit file pins ``HERMES_HOME`` for the actual gateway
      +    process, so we mirror that into our own environment to make
      +    ``read_runtime_status`` / ``get_running_pid`` read the correct files.
      +    """
      +    if not system:
      +        return
      +    env = _read_systemd_unit_environment(system=True)
      +    unit_home = env.get("HERMES_HOME", "").strip()
      +    if not unit_home:
      +        return
      +    current = os.environ.get("HERMES_HOME", "").strip()
      +    if current == unit_home:
      +        return
      +    os.environ["HERMES_HOME"] = unit_home
      +
      +
       def _read_systemd_unit_properties(
           system: bool = False,
           properties: tuple[str, ...] = (
      @@ -2380,6 +2440,7 @@ def systemd_stop(system: bool = False):
           if system:
               _require_root_for_system_service("stop")
           _require_service_installed("stop", system=system)
      +    _sync_hermes_home_from_systemd_unit(system=system)
           try:
               from gateway.status import get_running_pid, write_planned_stop_marker
               pid = get_running_pid(cleanup_stale=False)
      @@ -2408,6 +2469,7 @@ def systemd_restart(system: bool = False):
               _preflight_user_systemd()
           _require_service_installed("restart", system=system)
           refresh_systemd_unit_if_needed(system=system)
      +    _sync_hermes_home_from_systemd_unit(system=system)
           from gateway.status import get_running_pid
       
           pid = get_running_pid() or _systemd_main_pid(system=system)
      @@ -2503,6 +2565,8 @@ def systemd_status(deep: bool = False, system: bool = False, full: bool = False)
               print(f"  Run: {'sudo ' if system else ''}hermes gateway install{scope_flag}")
               return
       
      +    _sync_hermes_home_from_systemd_unit(system=system)
      +
           if has_conflicting_systemd_units():
               print_systemd_scope_conflict_warning()
               print()
    • scripts/release.py
      @@ -66,6 +66,7 @@
           "[redacted:email]": "qWaitCrypto",
           "[redacted:email]": "Qwinty",
           "[redacted:email]": "CalmProton",
      +    "[email protected]": "mbac",
           "[redacted:email]": "wesleysimplicio",
           "[redacted:email]": "SiliconID",
           "[redacted:email]": "xieNniu",

1 PR closed without merging

From the close comments: merged via another change 1

  • #22533 fix(gateway): adopt unit's HERMES_HOME for --system CLI ops · merged via another change → #22803
Closed issues · 1
  • Closed#22035[Bug]: gateway restart --system always reports failure (60s timeout × 2) — wrapper reads runtime status from root's HERMES_HOME
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.67: 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

Merged PRs
#22803
Closed issues
1
Attempts
1 PR closed without merging
Fix link checked
9 May 2026

For your agent

The same facts, typed.