{"record":{"id":"3cf8c1b50088e403","repo":"bytedance/deer-flow","slug":"openviking-memory-write-requires-thread-id","errorCode":null,"errorMessage":"OpenViking memory write requires thread_id","messagePattern":"OpenViking memory write requires thread_id","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"backend/packages/harness/deerflow/agents/memory/backends/openviking/openviking_manager.py","lineNumber":326,"sourceCode":"            self._close_requested = True\n            can_close = self._active_operations == 0\n        if can_close:\n            self._close_resources()\n\n    def _write_conversation(\n        self,\n        thread_id: str,\n        messages: list[Any],\n        *,\n        agent_name: str | None,\n        user_id: str | None,\n    ) -> None:\n        if not self._begin_operation():\n            logger.warning(\"OpenViking write ignored after backend shutdown\")\n            return\n        try:\n            if not thread_id:\n                raise ValueError(\"OpenViking memory write requires thread_id\")\n            peer_id = self._resolve_scope(user_id, agent_name)\n            session_id = _session_id(\n                self._config.owner_user_id,\n                peer_id,\n                thread_id,\n            )\n            with self._session_lock(session_id):\n                self._capture_locked(\n                    session_id,\n                    peer_id,\n                    _captureable_messages(\n                        messages,\n                        self._should_keep_hidden_message,\n                    ),\n                )\n        finally:\n            self._end_operation()\n","sourceCodeStart":308,"sourceCodeEnd":344,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/backend/packages/harness/deerflow/agents/memory/backends/openviking/openviking_manager.py#L308-L344","documentation":"OpenVikingMemoryManager's write path (_write_conversation, reached via add()/async add) raises ValueError when thread_id is empty. Each DeerFlow thread maps to one stable OpenViking Session keyed by thread_id, so an empty thread_id has no session to attach the capture to. The check runs after _begin_operation(), so it also cannot fire after shutdown (that path logs 'write ignored after backend shutdown' instead).","triggerScenarios":"Calling manager.add('' or None, messages) or the async equivalent directly; upstream, a middleware capture where thread_id resolution failed (e.g. a run without a LangGraph thread_id, or a caller invoking the embedded client without a thread).","commonSituations":"Custom integrations that call the memory manager directly without a thread context, or test harnesses that pass thread_id=None; also runs on a fresh graph invocation where no thread id was configured.","solutions":["Always supply a non-empty thread_id when calling add()/aadd() — in DeerFlow, use the LangGraph thread id of the conversation","If you see this from the embedded client, pass thread_id to DeerFlowClient.chat()/stream()","Guard callers: skip the memory write when thread_id is falsy rather than calling with ''"],"exampleFix":"# before\nmanager.add(thread_id='', messages=turn_messages, user_id='default')\n\n# after\nif thread_id:\n    manager.add(thread_id=thread_id, messages=turn_messages, user_id='default')","handlingStrategy":"validation","validationCode":"def safe_add(manager, thread_id, messages, **kw):\n    if not thread_id or not str(thread_id).strip():\n        logger.warning(\"skipping memory write: empty thread_id\")\n        return\n    manager.add(thread_id, messages, **kw)","typeGuard":"def has_thread_id(thread_id: object) -> bool:\n    return isinstance(thread_id, str) and bool(thread_id.strip())","tryCatchPattern":"try:\n    manager.add(thread_id, messages, user_id=user)\nexcept ValueError as exc:\n    if \"requires thread_id\" in str(exc):\n        logger.warning(\"memory capture skipped: no thread id\")\n    else:\n        raise","preventionTips":["Always create conversations under an explicit LangGraph thread_id","Guard custom callers of MemoryManager.add/aadd with a thread_id presence check","In tests, generate uuid4 thread ids rather than reusing '' "],"tags":["openviking","memory","thread-id","validation"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}