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
- 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
- Otherwise keep memory.mode: middleware (passive capture/injection)
- 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
- Before enabling memory.mode: tool, confirm the backend sets supports_search=True and overrides search()
- When writing custom backends, keep supports_search in lockstep with the search() override
- Construct managers through the validating factory so this fails at startup, not mid-run
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
- retrieval fact.id must be a non-empty string
- retrieval fact.content must be a non-empty string
- unsupported FTS5 retrieval mode: {mode}
- retrieval category filter must be a string
- fact.category must be a string
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/5cd484e370b6646d.
Report an issue: GitHub.