NousResearch/hermes-agent · error · SubagentLifecycleError

parent_session_id does not match the active session.

Error message

parent_session_id does not match the active session.

What it means

Raised by SubagentLifecycleManager.launch() when the caller supplies a parent_session_id on the launch request and it differs from the session_id of the actually-resolved parent agent. It is a consistency check tying a launch to the session the caller believes is active.

Source

Thrown at agent/subagent_lifecycle.py:207

    Running children are in-process only.  Completed results remain available
    until process exit; ``reconnect`` accurately reports that a serialized
    handle cannot reconnect after a restart instead of launching work again.
    """

    def __init__(self, parent_agent_resolver: Callable[[], Any]) -> None:
        self._parent_agent_resolver = parent_agent_resolver

    def launch(self, request: SubagentLaunchRequest) -> SubagentHandle:
        parent = self._parent_agent_resolver()
        if parent is None:
            raise SubagentLifecycleError(
                "No active Hermes parent session is available."
            )
        self._validate_request(request, parent)
        parent_session_id = str(getattr(parent, "session_id", "") or "") or None
        if request.parent_session_id and request.parent_session_id != parent_session_id:
            raise SubagentLifecycleError(
                "parent_session_id does not match the active session."
            )
        correlation_key = (parent_session_id, request.correlation_id or "")
        with _REGISTRY.lock:
            self._cleanup_locked()
            if request.correlation_id and correlation_key in _REGISTRY.correlations:
                raise SubagentLifecycleError(
                    "Duplicate correlation_id for this parent session."
                )

        # Delegate construction remains internal so plugin code never imports
        # private delegation helpers or manipulates the active-child registry.
        from tools.delegate_tool import (
            _build_child_preserving_parent_tools,
            DEFAULT_MAX_ITERATIONS,
        )

        child = _build_child_preserving_parent_tools(

View on GitHub (pinned to c896c09c42)

Solutions

  1. Omit parent_session_id (leave it None) when you want the launch bound to whatever session is currently active.
  2. If you must pin it, read the live value from the parent agent (parent.session_id) at launch time, not from a cached value.
  3. Discard and rebuild stale SubagentLaunchRequest objects whenever the session changes.

Example fix

# before
request = SubagentLaunchRequest(goal=g, parent_session_id=cached_session_id)
handle = manager.launch(request)

# after
request = SubagentLaunchRequest(goal=g, parent_session_id=str(parent.session_id))
handle = manager.launch(request)
Defensive patterns

Strategy: validation

Validate before calling

# simplest: don't pin the session at all
request = SubagentLaunchRequest(goal=goal)  # parent_session_id=None

# or pin to the live value:
live = str(getattr(parent, "session_id", "") or "")
request = SubagentLaunchRequest(goal=goal, parent_session_id=live or None)

Try / catch

try:
    manager.launch(request)
except SubagentLifecycleError as exc:
    if "parent_session_id does not match" in str(exc):
        request = dataclasses.replace(request, parent_session_id=None)
        handle = manager.launch(request)
    else:
        raise

Prevention

When it happens

Trigger: Passing SubagentLaunchRequest(parent_session_id=<old-id>) after the session was resumed/newed and got a different session_id; caching a request object built under a previous session and replaying it; hardcoding a session id in plugin code.

Common situations: Long-lived plugin code that snapshots the session id at startup and reuses it for later launches; resuming a conversation where Hermes assigned a new session_id; multi-session processes (gateway serving several chats) mixing up ids.

Related errors


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