hermes - 💡(How to fix) Fix [Bug] hermes-web-ui spawns redundant gateway processes on every startup, causing SIGTERM restart loops

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Root Cause

Two call sites both invoke startAll():

File 1: hermes-web-ui/dist/server/services/gateway-bootstrap.js

async function initGatewayManager() {
  const gatewayManager = new GatewayManager(activeProfile);
  setGatewayManager(gatewayManager);
  await gatewayManager.detectAllOnStartup();
  await gatewayManager.startAll();  // spawns gateway per profile
}

File 2: hermes-web-ui/dist/server/index.js (inside async _1())

bn = new I(l),
await bn.detectAllOnStartup(),
await bn.startAll()  // duplicate spawn call

startAll() iterates over all profiles and calls spawn("hermes gateway run --replace") for each. The --replace flag causes each new process to signal the previous one to terminate.

Fix Action

Workaround

Patch both files manually after each brew upgrade:

# Patch 1
sed -i '' 's/await gatewayManager.startAll();/\/\/ await gatewayManager.startAll(); \/\/ PATCHED/' \
  /opt/homebrew/lib/node_modules/hermes-web-ui/dist/server/services/gateway-bootstrap.js

# Patch 2
sed -i '' 's/await bn.startAll()/\/\/ await bn.startAll(); \/\/ PATCHED/' \
  /opt/homebrew/lib/node_modules/hermes-web-ui/dist/server/index.js

Code Example

node /opt/homebrew/lib/node_modules/hermes-web-ui/dist/server/index.js

---

async function initGatewayManager() {
  const gatewayManager = new GatewayManager(activeProfile);
  setGatewayManager(gatewayManager);
  await gatewayManager.detectAllOnStartup();
  await gatewayManager.startAll();  // spawns gateway per profile
}

---

bn = new I(l),
await bn.detectAllOnStartup(),
await bn.startAll()  // duplicate spawn call

---

// In both files:
await gatewayManager.detectAllOnStartup();
// await gatewayManager.startAll(); // Let launchd manage gateways

---

# Patch 1
sed -i '' 's/await gatewayManager.startAll();/\/\/ await gatewayManager.startAll(); \/\/ PATCHED/' \
  /opt/homebrew/lib/node_modules/hermes-web-ui/dist/server/services/gateway-bootstrap.js

# Patch 2
sed -i '' 's/await bn.startAll()/\/\/ await bn.startAll(); \/\/ PATCHED/' \
  /opt/homebrew/lib/node_modules/hermes-web-ui/dist/server/index.js
RAW_BUFFERClick to expand / collapse

[Bug] hermes-web-ui spawns redundant gateway processes on every startup, causing SIGTERM restart loops

Bug Description

hermes-web-ui (v0.5.25) calls gatewayManager.startAll() on every startup, which spawns a hermes gateway run --replace child process for every Hermes profile. These processes become orphaned when the web UI exits normally, getting re-parented to launchd (PPID=1). When a new web UI instance starts later, it spawns another batch — leading to accumulating redundant gateway processes and SIGTERM restart loops.

Environment

  • macOS (darwin, launchd init system)
  • Hermes Agent installed via brew install hermes-agent
  • hermes-web-ui v0.5.25
  • gateway running as a launchd-managed service (ai.hermes.gateway)
  • Platform: Apple Mac Mini M4, 24GB RAM

Reproduction Steps

  1. Ensure ai.hermes.gateway launchd service is running (the canonical gateway managed by launchd)
  2. Start hermes-web-ui:
    node /opt/homebrew/lib/node_modules/hermes-web-ui/dist/server/index.js
  3. Observe a new hermes gateway process appears (PID differs from launchd's gateway)
  4. Kill the web UI (Ctrl+C or exit)
  5. Note the new gateway process remains — PPID is now 1 (adopted by launchd)
  6. Repeat steps 2–5 → multiple orphaned gateway processes accumulate
  7. Each new hermes gateway run --replace sends SIGTERM to the previous instance, causing restart loops

Root Cause Analysis

Two call sites both invoke startAll():

File 1: hermes-web-ui/dist/server/services/gateway-bootstrap.js

async function initGatewayManager() {
  const gatewayManager = new GatewayManager(activeProfile);
  setGatewayManager(gatewayManager);
  await gatewayManager.detectAllOnStartup();
  await gatewayManager.startAll();  // spawns gateway per profile
}

File 2: hermes-web-ui/dist/server/index.js (inside async _1())

bn = new I(l),
await bn.detectAllOnStartup(),
await bn.startAll()  // duplicate spawn call

startAll() iterates over all profiles and calls spawn("hermes gateway run --replace") for each. The --replace flag causes each new process to signal the previous one to terminate.

Impact

  • Process accumulation: Each web UI start leaves behind ≥1 orphaned gateway process
  • Restart loops: The --replace flag sends SIGTERM to existing instances, triggering launchd's KeepAlive: SuccessfulExit=false restart policy
  • Memory waste: Each gateway instance consumes ~300MB+ RAM
  • Confusion: launchctl list shows multiple gateway processes with exit code 0 but status "Running" — hard to diagnose

Suggested Fix

The web UI should not be responsible for spawning gateway processes on macOS/launchd systems. It should connect to the already-running launchd-managed gateway via HTTP API.

Option A — Comment out startAll() calls (minimal risk, backward compatible):

// In both files:
await gatewayManager.detectAllOnStartup();
// await gatewayManager.startAll(); // Let launchd manage gateways

Option B — Make startAll() conditional on init system: The GatewayManager already detects launchd as the init system. In startAll(), skip spawning if CGI === "launchd" — on launchd systems the gateway should be started exclusively by its own launchd plist, not by web UI.

Workaround

Patch both files manually after each brew upgrade:

# Patch 1
sed -i '' 's/await gatewayManager.startAll();/\/\/ await gatewayManager.startAll(); \/\/ PATCHED/' \
  /opt/homebrew/lib/node_modules/hermes-web-ui/dist/server/services/gateway-bootstrap.js

# Patch 2
sed -i '' 's/await bn.startAll()/\/\/ await bn.startAll(); \/\/ PATCHED/' \
  /opt/homebrew/lib/node_modules/hermes-web-ui/dist/server/index.js

Additional Context

  • hermes gateway run --help on macOS outputs "Supported platforms: linux" — the run command may not even be intended for macOS at all (should use launchd or start subcommands instead)
  • The web UI does successfully detect already-running gateways via detectAllOnStartup() → health check path — so it can connect to an existing gateway without spawning a new one
  • Related: HERMES_WEB_UI_STOP_GATEWAYS_ON_SHUTDOWN environment variable controls whether web UI kills gateways on shutdown, but it does NOT prevent spawning on startup

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - 💡(How to fix) Fix [Bug] hermes-web-ui spawns redundant gateway processes on every startup, causing SIGTERM restart loops