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
- 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 ''
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
- 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 ''
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
- Invalid OpenViking peer scope: {agent_name!r}
- backend_unreachable
- request_failed
- Unable to recover SSE history after ${recoveryAttempts} atte
- Failed to submit feedback: ${res.status}
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/3cf8c1b50088e403.
Report an issue: GitHub.