NousResearch/hermes-agent · error · RuntimeError

already initialized

Error message

already initialized

What it means

RuntimeError('already initialized') from CodexAppServerClient.initialize() (agent/transports/codex_app_server.py:171). The client is a stateful JSON-RPC connection: initialize() sends the 'initialize' request plus the 'initialized' notification and then sets _initialized = True. Calling it a second time on the same client is rejected because the handshake is once-per-connection by protocol.

Source

Thrown at agent/transports/codex_app_server.py:171

        self._reader = threading.Thread(target=self._read_stdout, daemon=True)
        self._reader.start()
        self._stderr_reader = threading.Thread(target=self._read_stderr, daemon=True)
        self._stderr_reader.start()

    # ---------- lifecycle ----------

    def initialize(
        self,
        client_name: str = "hermes",
        client_title: str = "Hermes Agent",
        client_version: str = "0.1",
        capabilities: Optional[dict] = None,
        timeout: float = 10.0,
    ) -> dict:
        """Send `initialize` + `initialized` handshake. Returns the server's
        InitializeResponse (userAgent, codexHome, platformFamily, platformOs)."""
        if self._initialized:
            raise RuntimeError("already initialized")
        params = {
            "clientInfo": {
                "name": client_name,
                "title": client_title,
                "version": client_version,
            },
            "capabilities": capabilities or {},
        }
        result = self.request("initialize", params, timeout=timeout)
        self.notify("initialized")
        self._initialized = True
        return result

    def close(self, timeout: float = 3.0) -> None:
        """Close stdin and wait for the subprocess to exit, escalating to kill."""
        if self._closed:
            return
        self._closed = True

View on GitHub (pinned to c896c09c42)

Solutions

  1. Guard the second call: skip initialize() when the client reports it is already initialized (check the client's _initialized state or cache the handshake result yourself).
  2. For a genuine re-handshake, create a fresh CodexAppServerClient (new process/connection) instead of reusing the old one.
  3. Fix retry wrappers to only retry when the first attempt provably failed (exception propagated), not on ambiguity.

Example fix

# before
client.initialize()          # ok
client.initialize()          # RuntimeError: already initialized

# after
if not client._initialized:  # or expose a public `initialized` property
    client.initialize()
Defensive patterns

Strategy: validation

Validate before calling

def ensure_initialized(client, **kw) -> dict:
    if client._initialized:
        return {}  # handshake already done
    return client.initialize(**kw)

Try / catch

try:
    client.initialize()
except RuntimeError as exc:
    if "already initialized" in str(exc):
        pass  # idempotent use; safe to continue
    else:
        raise

Prevention

When it happens

Trigger: Calling client.initialize(...) twice on the same CodexAppServerClient instance — e.g. a retry wrapper that re-runs the whole setup on timeout, or two code paths (session start + resume) both performing the handshake.

Common situations: Retry/reconnect logic that treats a slow initialize as 'not done' and re-invokes it; refactoring where a shared client gets initialized once at startup and again per session.

Related errors


AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14). Data as JSON: /api/errors/bdfebfc9886d066f. Report an issue: GitHub.