bytedance/deer-flow · error · ValueError

Invalid OpenViking peer scope: {agent_name!r}

Error message

Invalid OpenViking peer scope: {agent_name!r}

What it means

_canonical_peer_id() in session.py raises ValueError when the supplied agent_name, after strip().lower(), is empty or equals the reserved default scope '__default__'. Agent names are mapped to disjoint OpenViking actor peer IDs; the default scope is reserved for the manager-level default (agent_name=None) and cannot be passed explicitly, and an empty name has no peer identity.

Source

Thrown at backend/packages/harness/deerflow/agents/memory/backends/openviking/session.py:28

from .config import GENERATED_PEER_PREFIX, is_safe_peer_id

_SESSION_NAMESPACE = "deerflow-openviking-adapter-v1"
_DEFAULT_AGENT_SCOPE = "__default__"
_CURSOR_SCHEMA_VERSION = 1


def _canonical_peer_id(
    agent_name: str | None,
    default_peer_id: str,
) -> str:
    """Map DeerFlow's case-insensitive agent names to disjoint peer IDs."""

    if agent_name is None:
        return default_peer_id

    value = str(agent_name).strip().lower()
    if not value or value == _DEFAULT_AGENT_SCOPE:
        raise ValueError(f"Invalid OpenViking peer scope: {agent_name!r}")
    if is_safe_peer_id(value) and value != default_peer_id and not value.startswith(GENERATED_PEER_PREFIX):
        return value

    # The generated namespace is reserved, so compatible names, the default
    # peer, and hashed fallbacks cannot alias one another. The 128-bit digest
    # also avoids collisions caused by sanitizing or truncating agent names.
    digest = hashlib.sha256(value.encode("utf-8")).hexdigest()[:32]
    return f"{GENERATED_PEER_PREFIX}{digest}"


def _session_id(
    owner_user_id: str,
    peer_id: str,
    thread_id: str,
) -> str:
    """Derive one stable OpenViking session for one DeerFlow thread."""

    digest = hashlib.sha256(f"{_SESSION_NAMESPACE}\0{owner_user_id}\0{peer_id}\0{thread_id}".encode()).hexdigest()

View on GitHub (pinned to 1dd6ba1acb)

Solutions

  1. Pass agent_name=None when you mean the default peer — the manager substitutes config default_peer_id
  2. Use a real non-empty agent name; any unsafe characters are automatically hashed, so no sanitization is needed
  3. Never forward DeerMem's '__default__' sentinel to OpenViking-backed operations

Example fix

# before
manager.add(thread_id, msgs, agent_name='__default__', user_id='default')

# after
manager.add(thread_id, msgs, agent_name=None, user_id='default')
Defensive patterns

Strategy: validation

Validate before calling

def safe_agent_name(agent_name: str | None) -> str | None:
    if agent_name is None:
        return None
    normalized = str(agent_name).strip().lower()
    if not normalized or normalized == "__default__":
        return None  # or raise: explicit default scope is not a valid peer
    return agent_name

Type guard

def is_valid_explicit_peer(agent_name: object) -> bool:
    if not isinstance(agent_name, str):
        return agent_name is None
    v = agent_name.strip().lower()
    return bool(v) and v != "__default__"

Prevention

When it happens

Trigger: Calling memory operations with agent_name='' , agent_name=' ', or agent_name='__default__' (case-insensitive). Other unsafe names do not raise — they are hashed into the reserved 'df-agent-' namespace instead; only these two degenerate forms are rejected.

Common situations: Passing the DeerMem sentinel '__default__' explicitly through the OpenViking backend (only DeerMem storage accepts it); a caller building agent_name from empty config or an unset subagent name.

Related errors


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