{"record":{"id":"5395f1fdaad075da","repo":"NousResearch/hermes-agent","slug":"duplicate-correlation-id-for-this-parent-session","errorCode":null,"errorMessage":"Duplicate correlation_id for this parent session.","messagePattern":"Duplicate correlation_id for this parent session\\.","errorType":"exception","errorClass":"SubagentLifecycleError","httpStatus":null,"severity":"error","filePath":"agent/subagent_lifecycle.py","lineNumber":214,"sourceCode":"        self._parent_agent_resolver = parent_agent_resolver\n\n    def launch(self, request: SubagentLaunchRequest) -> SubagentHandle:\n        parent = self._parent_agent_resolver()\n        if parent is None:\n            raise SubagentLifecycleError(\n                \"No active Hermes parent session is available.\"\n            )\n        self._validate_request(request, parent)\n        parent_session_id = str(getattr(parent, \"session_id\", \"\") or \"\") or None\n        if request.parent_session_id and request.parent_session_id != parent_session_id:\n            raise SubagentLifecycleError(\n                \"parent_session_id does not match the active session.\"\n            )\n        correlation_key = (parent_session_id, request.correlation_id or \"\")\n        with _REGISTRY.lock:\n            self._cleanup_locked()\n            if request.correlation_id and correlation_key in _REGISTRY.correlations:\n                raise SubagentLifecycleError(\n                    \"Duplicate correlation_id for this parent session.\"\n                )\n\n        # Delegate construction remains internal so plugin code never imports\n        # private delegation helpers or manipulates the active-child registry.\n        from tools.delegate_tool import (\n            _build_child_preserving_parent_tools,\n            DEFAULT_MAX_ITERATIONS,\n        )\n\n        child = _build_child_preserving_parent_tools(\n            task_index=0,\n            goal=request.goal,\n            context=request.context,\n            toolsets=list(request.allowed_toolsets)\n            if request.allowed_toolsets\n            else None,\n            model=request.model,","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/agent/subagent_lifecycle.py#L196-L232","documentation":"Raised by SubagentLifecycleManager.launch() when a launch request carries a correlation_id whose (parent_session_id, correlation_id) key is already registered and not yet cleaned up. It prevents two live children being correlated to the same logical task within one parent session.","triggerScenarios":"Launching twice with the same correlation_id while the first child is still running (or its registry entry has not been reaped); reusing an id generator that repeats after a crash; firing a webhook handler twice for the same event and mapping event-id to correlation_id.","commonSituations":"Retries of a failed launch that reuse the request verbatim; at-least-once delivery from a queue where the same message triggers two launches; generating correlation ids from timestamps with second granularity causing collisions.","solutions":["Generate a fresh correlation_id (e.g. uuid4().hex) for every launch attempt, including retries.","If idempotency-by-correlation-id is desired, first check the existing entry's state (poll/wait for completion) instead of launching again — completed entries are pruned by cleanup, after which the id is reusable.","Log the correlation_id at launch so duplicates can be traced to their first owner."],"exampleFix":"# before\nrequest = SubagentLaunchRequest(goal=g, correlation_id=event_id)\nhandle = manager.launch(request)  # second delivery of event -> duplicate\n\n# after\nimport uuid\nrequest = SubagentLaunchRequest(goal=g, correlation_id=f\"{event_id}:{uuid.uuid4().hex[:8]}\")\nhandle = manager.launch(request)","handlingStrategy":"validation","validationCode":"import uuid\nrequest = SubagentLaunchRequest(\n    goal=goal,\n    correlation_id=uuid.uuid4().hex,  # unique per attempt, not per logical event\n)","typeGuard":null,"tryCatchPattern":"try:\n    handle = manager.launch(request)\nexcept SubagentLifecycleError as exc:\n    if \"Duplicate correlation_id\" in str(exc):\n        request = dataclasses.replace(request, correlation_id=uuid.uuid4().hex)\n        handle = manager.launch(request)\n    else:\n        raise","preventionTips":["Generate correlation ids with uuid4 per launch attempt, including retries.","For at-least-once event sources, append an attempt counter to the event id.","Poll existing children by correlation before re-launching the same logical task."],"tags":["subagents","delegation","idempotency","registry"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}