{"record":{"id":"a4f6c4b971dff275","repo":"NousResearch/hermes-agent","slug":"could-not-reach-the-management-api-at-host-port","errorCode":null,"errorMessage":"could not reach the management API at {host}:{port} ({exc}).  If the daemon was started before reload support, run `hermes egress restart` once.","messagePattern":"could not reach the management API at (.+?):(.+?) \\((.+?)\\)\\.  If the daemon was started before reload support, run `hermes egress restart` once\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"agent/proxy_sources/iron_proxy.py","lineNumber":988,"sourceCode":"            pass\n        if exc.code == 422:\n            raise RuntimeError(\n                f\"iron-proxy rejected the new config (validation failed; \"\n                f\"the running ruleset is unchanged): {body}\"\n            ) from exc\n        if exc.code == 401:\n            raise RuntimeError(\n                \"management API rejected our key (401).  The running \"\n                \"daemon was started with a different management.token — \"\n                \"run `hermes egress restart`.\"\n            ) from exc\n        raise RuntimeError(\n            f\"management reload failed (HTTP {exc.code}): {body}\"\n        ) from exc\n    except (urllib.error.URLError, OSError) as exc:\n        # A daemon started from a pre-management config is alive but has\n        # no listener on the management port.\n        raise RuntimeError(\n            f\"could not reach the management API at {host}:{port} ({exc}).  \"\n            \"If the daemon was started before reload support, run \"\n            \"`hermes egress restart` once.\"\n        ) from exc\n\n\ndef _default_http_listen(tunnel_port: int) -> List[str]:\n    \"\"\"Build the single host:port bind the proxy should listen on.\n\n    iron-proxy v0.39 supports exactly ONE ``proxy.http_listen`` bind per\n    daemon process, so this returns a one-element list and the choice of\n    host matters:\n\n    * **Linux:** bind the docker bridge gateway (``172.17.0.1`` by\n      default).  Sandboxes reach the proxy via\n      ``host.docker.internal:host-gateway``, which Docker resolves to\n      exactly this bridge gateway IP on Linux — a loopback-only bind is\n      unreachable from inside containers there.  The bridge IP is still","sourceCodeStart":970,"sourceCodeEnd":1006,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/agent/proxy_sources/iron_proxy.py#L970-L1006","documentation":"POST to http://<host>:<port>/v1/reload failed at the transport layer (URLError/OSError): connection refused, timeout, or reset. The comment in the code names the classic cause — a daemon started from a pre-management config is alive (pid checks pass) but has no listener on the management port. The message tells the user to do the one-time restart.","triggerScenarios":"reload_proxy() when nothing accepts the connection on the management host:port — daemon predates management support, daemon hung/dead but pid reused by another process (so _pid_alive passes), or the management listener binds a different address than the config says.","commonSituations":"Post-upgrade first reload on a long-running daemon; host reboot where the pid was recycled; firewall/nftables rules on loopback exotic setups blocking the port.","solutions":["Run `hermes egress restart` once — this both upgrades the daemon to a management-capable config and re-binds the listener.","If it persists, check the pid actually belongs to iron-proxy (ps -p <pid>) and that the management host:port in proxy.yaml is loopback-reachable (ss -ltnp).","Verify daemon health from iron-proxy.log for hangs."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"import socket\n\ndef mgmt_listener_reachable(host: str, port: int, timeout: float = 2.0) -> bool:\n    try:\n        with socket.create_connection((host, port), timeout=timeout):\n            return True\n    except OSError:\n        return False","typeGuard":null,"tryCatchPattern":"try:\n    reload_proxy()\nexcept RuntimeError as e:\n    if \"could not reach the management API\" in str(e):\n        # pre-management daemon or dead listener — one restart fixes both\n        raise","preventionTips":["Do the one-time `hermes egress restart` after upgrading to a management-capable version.","Health-check the management port (not just the pid) before reload-driven automation."],"tags":["network","daemon","migration","iron-proxy"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}