bytedance/deer-flow · error · ValueError

memory mode='tool' requires a backend that implements search

Error message

memory mode='tool' requires a backend that implements search(), but {type(self).__name__} does not override search(). Use mode='middleware' or a backend that overrides search() (and sets supports_search=True).

What it means

MemoryManager's model_post_init validation rejects mode='tool' when the concrete backend does not override the base search() method. Tool mode registers memory_search as a model tool, which requires a real retrieval implementation; a backend inheriting the base NotImplementedError search() cannot serve it. The same validator also enforces that supports_search matches whether search() is overridden.

Source

Thrown at backend/packages/harness/deerflow/agents/memory/manager.py:193

        invariants (e.g. storage_path is a directory) stay on ``DeerMemConfig``.

        ``supports_search`` (ClassVar flag) must match whether ``search()`` is
        actually overridden, so the declarative flag can't drift from the
        implementation -- a backend that overrides ``search()`` but forgets
        ``supports_search = True`` (or sets the flag without overriding) is a bug
        caught at instantiation, not a misleading tool-mode rejection or a runtime
        ``NotImplementedError`` on the first ``memory_search`` call.
        """
        search_overridden = type(self).search is not MemoryManager.search
        if type(self).supports_search != search_overridden:
            raise ValueError(
                f"{type(self).__name__}.supports_search={type(self).supports_search} "
                f"is inconsistent with search(): search() is "
                f"{'overridden' if search_overridden else 'inherited (not implemented)'}. "
                f"Set supports_search={search_overridden} on the backend to match."
            )
        if self.mode == "tool" and not search_overridden:
            raise ValueError(
                f"memory mode='tool' requires a backend that implements search(), but {type(self).__name__} does not override search(). Use mode='middleware' or a backend that overrides search() (and sets supports_search=True)."
            )
        return self

    # ── Tier 1: @abstractmethod ─────────────────────────────────────────
    # Every backend MUST implement these (write + read-inject are the backend's
    # fundamental duties). Missing one is a severe bug (memory is persistent
    # state) -- @abstractmethod catches it at instantiation. noop implements
    # them as no-op / "".
    @abstractmethod
    def add(
        self,
        thread_id: str,
        messages: list[Any],
        *,
        agent_name: str | None = None,
        user_id: str | None = None,
        trace_id: str | None = None,

View on GitHub (pinned to 1dd6ba1acb)

Solutions

  1. If you need tool mode, switch to a backend that implements search() (e.g. DeerMem or honcho set supports_search=True), or implement search() on your custom backend and set supports_search=True
  2. Otherwise keep memory.mode: middleware (passive capture/injection)
  3. For custom backends, verify supports_search == (type(self).search is not MemoryManager.search) so the consistency check passes

Example fix

# before: custom backend claims tool support without search()
class MyBackend(MemoryManager):
    supports_search = True   # but search() not overridden -> validator error

# after
class MyBackend(MemoryManager):
    supports_search = True
    def search(self, query, top_k=5, *, user_id=None, agent_name=None, category=None):
        ...  # real retrieval implementation
Defensive patterns

Strategy: validation

Validate before calling

from deerflow.agents.memory.manager import MemoryManager
backend_type = resolve_manager_class(memory_cfg)
search_overridden = backend_type.search is not MemoryManager.search
if memory_cfg.get("mode", "middleware") == "tool":
    assert search_overridden, f"{backend_type.__name__} cannot serve mode='tool': no search() override"
assert backend_type.supports_search == search_overridden

Type guard

def backend_supports_tool_mode(backend_type: type) -> bool:
    return backend_type.search is not MemoryManager.search and backend_type.supports_search is True

Prevention

When it happens

Trigger: Constructing any MemoryManager subclass with mode='tool' while the subclass leaves search() unimplemented and/or supports_search inconsistent — e.g. enabling memory.mode: tool with a backend that only does passive capture. Caught at instantiation, deliberately before the first memory_search tool call could hit NotImplementedError.

Common situations: Flipping memory.mode to tool in config.yaml without checking backend capabilities; writing a custom MemoryManager backend and forgetting to implement search() while setting supports_search=True (that direction instead gives the supports_search-inconsistency ValueError from the same validator).

Related errors


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