NousResearch/hermes-agent · error · CodexAppServerError

-32603

-32603

Error message

codex thread/start returned no thread id (payload keys: {sorted(result.keys())})

What it means

CodexAppServerError (code -32603, internal error) from the thread/start handling in agent/transports/codex_app_server_session.py:359. The session layer tolerates several codex serialization variants (thread.id, thread.sessionId, top-level sessionId, threadId) but if none of them yields an id it refuses to continue, reporting which keys the payload actually had so the mismatch is diagnosable.

Source

Thrown at agent/transports/codex_app_server_session.py:359

        # configured otherwise in their codex config.toml) is the standard
        # codex CLI workflow and avoids fighting codex's own validation.
        # Users who want a write-capable profile configure it in their
        # ~/.codex/config.toml the same way they would for any codex usage.
        params: dict[str, Any] = {"cwd": self._cwd}
        result = self._client.request("thread/start", params, timeout=15)
        # Cross-fill thread.id/sessionId — different codex versions have
        # serialized this under either key. Mirrors openclaw beta.8's
        # tolerance fix so future codex drops/renames don't KeyError us
        # at handshake time.
        thread_obj = result.get("thread") or {}
        thread_id = (
            thread_obj.get("id")
            or thread_obj.get("sessionId")
            or result.get("sessionId")
            or result.get("threadId")
        )
        if not thread_id:
            raise CodexAppServerError(
                code=-32603,
                message=(
                    "codex thread/start returned no thread id "
                    f"(payload keys: {sorted(result.keys())})"
                ),
            )
        self._thread_id = thread_id
        logger.info(
            "codex app-server thread started: id=%s profile=%s cwd=%s",
            self._thread_id[:8],
            self._permission_profile,
            self._cwd,
        )
        return self._thread_id

    def close(self) -> None:
        if self._closed:
            return

View on GitHub (pinned to c896c09c42)

Solutions

  1. Read the payload keys in the message and compare against the four tolerated keys (id/sessionId/threadId) to confirm the schema moved.
  2. Pin the codex app-server to a version this client is tested against (or upgrade hermes-agent to a release supporting the new schema).
  3. If you maintain a fork, add the new key to the cross-fill chain in codex_app_server_session.py and upstream it.
  4. Check codex's own logs for why thread/start may have returned a degenerate payload (e.g. auth/config error).

Example fix

# before (tolerated keys only)
thread_id = (
    thread_obj.get("id") or thread_obj.get("sessionId")
    or result.get("sessionId") or result.get("threadId")
)

# after (extend for a newer codex schema)
thread_id = (
    thread_obj.get("id") or thread_obj.get("sessionId")
    or result.get("sessionId") or result.get("threadId")
    or result.get("conversationId")
)
Defensive patterns

Strategy: try-catch

Validate before calling

_TOLERATED_KEYS = ("thread", "sessionId", "threadId")

def thread_start_payload_looks_ok(result: dict) -> bool:
    t = result.get("thread") or {}
    return bool(t.get("id") or t.get("sessionId") or result.get("sessionId") or result.get("threadId"))

Try / catch

from agent.transports.codex_app_server import CodexAppServerError

try:
    session.start_thread()
except CodexAppServerError as exc:
    if exc.code == -32603 and "no thread id" in exc.message:
        raise RuntimeError(
            f"codex schema mismatch (keys: {exc.message}); pin/upgrade codex"
        ) from exc
    raise

Prevention

When it happens

Trigger: A codex app-server version whose thread/start response schema moved the thread id to a new key not in the tolerated list (or omits it entirely on failure), typically right after upgrading/downgrading codex.

Common situations: Version skew: the pinned codex version changed its JSON-RPC payload shape; a partially-failed thread/start that returns an error-ish dict without an id; nightly/rolling codex installs.

Related errors


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