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 = TrueView on GitHub (pinned to c896c09c42)
Solutions
- 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).
- For a genuine re-handshake, create a fresh CodexAppServerClient (new process/connection) instead of reusing the old one.
- 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
- Centralize the handshake in one place (session start) instead of multiple call sites.
- For reconnects, build a new client rather than re-initializing.
- Expose an `initialized` property on the client so callers need no try/except.
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
- codex app-server method {method!r} timed out after {timeout}
- codex app-server error {code}: {message}
- codex app-server client is closed
- codex app-server stdin not available
- -32603
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/bdfebfc9886d066f.
Report an issue: GitHub.