bytedance/deer-flow · error · ValueError

OpenViking memory write requires thread_id

Error message

OpenViking memory write requires thread_id

What it means

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).

Source

Thrown at backend/packages/harness/deerflow/agents/memory/backends/openviking/openviking_manager.py:326

            self._close_requested = True
            can_close = self._active_operations == 0
        if can_close:
            self._close_resources()

    def _write_conversation(
        self,
        thread_id: str,
        messages: list[Any],
        *,
        agent_name: str | None,
        user_id: str | None,
    ) -> None:
        if not self._begin_operation():
            logger.warning("OpenViking write ignored after backend shutdown")
            return
        try:
            if not thread_id:
                raise ValueError("OpenViking memory write requires thread_id")
            peer_id = self._resolve_scope(user_id, agent_name)
            session_id = _session_id(
                self._config.owner_user_id,
                peer_id,
                thread_id,
            )
            with self._session_lock(session_id):
                self._capture_locked(
                    session_id,
                    peer_id,
                    _captureable_messages(
                        messages,
                        self._should_keep_hidden_message,
                    ),
                )
        finally:
            self._end_operation()

View on GitHub (pinned to 1dd6ba1acb)

Solutions

  1. Always supply a non-empty thread_id when calling add()/aadd() — in DeerFlow, use the LangGraph thread id of the conversation
  2. If you see this from the embedded client, pass thread_id to DeerFlowClient.chat()/stream()
  3. Guard callers: skip the memory write when thread_id is falsy rather than calling with ''

Example fix

# before
manager.add(thread_id='', messages=turn_messages, user_id='default')

# after
if thread_id:
    manager.add(thread_id=thread_id, messages=turn_messages, user_id='default')
Defensive patterns

Strategy: validation

Validate before calling

def safe_add(manager, thread_id, messages, **kw):
    if not thread_id or not str(thread_id).strip():
        logger.warning("skipping memory write: empty thread_id")
        return
    manager.add(thread_id, messages, **kw)

Type guard

def has_thread_id(thread_id: object) -> bool:
    return isinstance(thread_id, str) and bool(thread_id.strip())

Try / catch

try:
    manager.add(thread_id, messages, user_id=user)
except ValueError as exc:
    if "requires thread_id" in str(exc):
        logger.warning("memory capture skipped: no thread id")
    else:
        raise

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14). Data as JSON: /api/errors/3cf8c1b50088e403. Report an issue: GitHub.