NousResearch/hermes-agent · error · RuntimeError
iron-proxy exited immediately (code {proc.returncode}). Last
Error message
iron-proxy exited immediately (code {proc.returncode}). Last log lines:
{tail} What it means
During the startup grace poll loop, proc.poll() returned non-None before the tunnel port ever listened — the iron-proxy process died immediately. The pidfile (written right after Popen) is unlinked and the last 20 log lines are attached so the config/runtime failure is visible. This is the 'died early' branch inside the do-while listening check.
Source
Thrown at agent/proxy_sources/iron_proxy.py:1954
install_handlers = (
platform.system() != "Windows"
and threading.current_thread() is threading.main_thread()
)
if install_handlers:
prev_sigint = signal.signal(signal.SIGINT, _interrupt_handler)
prev_sigterm = signal.signal(signal.SIGTERM, _interrupt_handler)
try:
deadline = time.time() + _STARTUP_GRACE_SECONDS
# Do-while shape: check listening at least once even when the
# grace window is 0 (test harness / synchronous fast-path).
while True:
if proc.poll() is not None:
tail = _tail_log(log_path, lines=20)
try:
pidfile.unlink()
except FileNotFoundError:
pass
raise RuntimeError(
f"iron-proxy exited immediately (code {proc.returncode}). "
f"Last log lines:\n{tail}"
)
if _port_listening(probe_host, tunnel_port):
listening = True
break
if time.time() >= deadline:
break
time.sleep(0.1)
finally:
if install_handlers:
signal.signal(signal.SIGINT, prev_sigint)
signal.signal(signal.SIGTERM, prev_sigterm)
# Final exit check — process may have died right at deadline.
if proc.poll() is not None:
tail = _tail_log(log_path, lines=20)
try:View on GitHub (pinned to c896c09c42)
Solutions
- Read the 'Last log lines' in the message — the proxy's own error is almost always the first line there
- Fix the reported config problem in the generated config / config.yaml and retry `hermes egress start`
- If the log shows 'address already in use', free the port or change tunnel_port, then retry
- If the schema changed after an upgrade, re-run `hermes egress setup` to regenerate the config
Example fix
# before: config references a missing cert # RuntimeError: iron-proxy exited immediately (code 1). Last log lines: # error: cannot open cert file /etc/ssl/wrong.pem # fix path in config, then: hermes egress start # after: proxy stays alive through the grace window and binds the port
Defensive patterns
Strategy: try-catch
Validate before calling
import yaml
from pathlib import Path
def config_parses(cfg: Path) -> bool:
try:
yaml.safe_load(cfg.read_text())
return True
except yaml.YAMLError:
return False
# and pre-check the port is free:
import socket
def port_free(host: str, port: int) -> bool:
with socket.socket() as s:
return s.connect_ex((host, port)) != 0 Try / catch
try:
start_proxy(...)
except RuntimeError as e:
if 'exited immediately' in str(e):
tail = str(e).split('Last log lines:', 1)[-1]
diagnose_and_fix(tail) # fix config/port/cert, then retry once Prevention
- Never hand-edit the generated proxy config without validating YAML first
- Pre-flight the tunnel port with a socket connect before start
- After upgrading the proxy binary, regenerate config via setup (schema drift)
When it happens
Trigger: start_proxy() where the proxy binary rejects its config (bad YAML, unknown fields), cannot bind its port, cannot read a referenced cert/key file, or crashes on startup — all within _STARTUP_GRACE_SECONDS.
Common situations: Hand-edited config.yaml with a syntax or schema error; port already in use by another service; TLS key/cert paths wrong; a proxy version whose config schema changed after an upgrade; missing capabilities for binding a low port.
Related errors
- SSH remote mode is selected but no host is configured.
- iron-proxy did not bind {probe_host}:{tunnel_port} within {_
- Bitwarden refresh did not return secrets for {missing}. Eit
- credential_source=bitwarden but the access-token env or proj
- Hermes install at ${ACTIVE_HERMES_ROOT} is missing or incomp
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/aa4eacca2bf140c6.
Report an issue: GitHub.