bytedance/deer-flow · error · ValueError
retrieval category filter must be a string
Error message
retrieval category filter must be a string
What it means
Raised by the FTS5 retrieval path in deermem when a caller passes a filters dict whose 'category' value is present but not a string. The engine's SQL search binds category as a text parameter, so non-string values (int, list, dict, bool) are rejected before querying. This is an input-contract error on the public search/retrieve API surface.
Source
Thrown at backend/packages/harness/deerflow/agents/memory/backends/deermem/deermem/core/retrieval.py:626
def search(
self,
query: str,
*,
scopes: list[dict[str, str | None]],
top_k: int,
mode: str,
filters: dict[str, Any] | None,
) -> list[dict[str, Any]]:
if not query.strip() or top_k <= 0:
return []
if mode not in {"hybrid", "fts5", "lexical"}:
raise ValueError(f"unsupported FTS5 retrieval mode: {mode}")
filters = filters or {}
category = filters.get("category")
if category is not None and not isinstance(category, str):
raise ValueError("retrieval category filter must be a string")
results: list[dict[str, Any]] = []
per_scope_limit = top_k * 4
for scope in scopes:
scope_user, scope_agent = _scope_key(scope)
for candidate in self._engine.search(
query,
scope_user=scope_user,
scope_agent=scope_agent,
category=category,
top_k=per_scope_limit,
):
fact = dict(candidate)
score = float(fact.pop("score", 0.0))
bm25_score = float(fact.pop("bm25_score", 0.0))
if any(fact.get(key) != value for key, value in filters.items()):
continue
results.append(View on GitHub (pinned to 1dd6ba1acb)
Solutions
- Coerce or reject the category before calling search: use a single string, e.g. filters={'category': str(value)} only after confirming it is scalar.
- If the caller has a list of categories, issue one search per category and merge/limit results, since the backend accepts exactly one.
- Validate the whole filters dict against the documented schema ({category?: str}) at the API boundary and return a 4xx to the client instead of letting the backend raise.
Example fix
// before
results = memory.search(query="user preferences", top_k=5, mode="hybrid", filters={"category": ["preference", "context"]})
# after (python)
results = []
for category in ["preference", "context"]:
results.extend(memory.search(query="user preferences", top_k=5, mode="hybrid", filters={"category": category})) Defensive patterns
Strategy: validation
Validate before calling
category = (filters or {}).get("category")
if category is not None and not isinstance(category, str):
if isinstance(category, (list, tuple)):
raise TypeError("run one search per category; backend accepts a single string")
category = str(category)
filters = {"category": category} if category is not None else {} Type guard
from typing import Any
def is_valid_category_filter(filters: dict[str, Any] | None) -> bool:
category = (filters or {}).get("category")
return category is None or isinstance(category, str) Try / catch
try:
results = backend.search(query, top_k=5, mode="hybrid", filters=filters)
except ValueError as exc:
if "category filter" in str(exc):
raise # fix the filter shape upstream; surface 4xx to the client
raise Prevention
- Type-check filters at the API boundary before forwarding to the retrieval backend.
- Never pass multi-select category arrays; loop categories and merge results.
- Keep a typed schema (TypedDict/dataclass) for the filters argument in caller code.
When it happens
Trigger: Calling memory search/retrieve with filters={'category': 5}, filters={'category': ['preference']}, or filters={'category': True}. Any code that forwards untyped user or LLM tool output straight into the filters argument of the retrieval backend hits this immediately.
Common situations: LLM tool-calls that emit JSON numbers or arrays where the schema expects a single string; frontends forwarding a multi-select category picker as a list; refactors that changed the filter value type without updating callers.
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}
- fact.category must be a string
- fact.{field} must be a list of strings
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/dad76dc2a409488c.
Report an issue: GitHub.