bytedance/deer-flow · error · TypeError
guaranteed_categories must be an iterable of strings, not a
Error message
guaranteed_categories must be an iterable of strings, not a bare str
What it means
TypeError from format_memory_prompt (deermem): guaranteed_categories was passed as a bare str. Iterating a string yields characters, which would silently build a meaningless frozenset of letters and disable the guarantee with no warning, so the helper rejects strings explicitly. Config-layer callers are protected by Pydantic list[str]; this guard covers direct calls to the public helper.
Source
Thrown at backend/packages/harness/deerflow/agents/memory/backends/deermem/deermem/core/prompt.py:507
the budget becomes truly additive only when the guaranteed lines
alone would push the assembled output past *max_tokens*, at which
point the safety-truncation ceiling is raised to
``max_tokens + guaranteed_actual_usage`` to protect them.
Ignored when *guaranteed_categories* is ``None`` or empty.
Returns:
Formatted memory string for system prompt injection.
"""
if not memory_data:
return ""
# Reject a bare string explicitly: iterating a ``str`` yields single
# characters, which would silently produce a meaningless frozenset of
# letters and turn the guarantee off without any warning. Config-layer
# callers go through Pydantic (which enforces ``list[str]``), so this
# only guards the public helper surface.
if isinstance(guaranteed_categories, str):
raise TypeError("guaranteed_categories must be an iterable of strings, not a bare str")
effective_guaranteed: frozenset[str] = frozenset(c.strip() for c in guaranteed_categories if isinstance(c, str) and c.strip()) if guaranteed_categories else frozenset()
sections: list[str] = []
# Format user context
user_data = memory_data.get("user", {})
if user_data:
user_sections = []
work_ctx = user_data.get("workContext", {})
if work_ctx.get("summary"):
user_sections.append(f"Work: {_escape_summary(work_ctx['summary'])}")
personal_ctx = user_data.get("personalContext", {})
if personal_ctx.get("summary"):
user_sections.append(f"Personal: {_escape_summary(personal_ctx['summary'])}")
top_of_mind = user_data.get("topOfMind", {})View on GitHub (pinned to 1dd6ba1acb)
Solutions
- Pass a list/tuple of category strings: guaranteed_categories=["preferences"].
- If the variable may be either, normalize: cats = [cats] if isinstance(cats, str) else cats.
- Type the producing field as list[str] in Pydantic/dataclass so the mistake is caught at validation time.
Example fix
# before format_memory_prompt(data, guaranteed_categories="identity") # after format_memory_prompt(data, guaranteed_categories=["identity"])
Defensive patterns
Strategy: type-guard
Validate before calling
cats = guaranteed_categories
if isinstance(cats, str):
cats = [cats] # or raise early with your own clearer message
formatted = format_memory_prompt(memory_data, guaranteed_categories=cats) Type guard
def is_str_iterable_not_str(v) -> bool:
return not isinstance(v, str) and isinstance(v, (list, tuple, set, frozenset)) and all(isinstance(x, str) for x in v) Prevention
- Type the field list[str] in Pydantic models so validation catches it at config load.
- Never pass a single category as a scalar; always wrap in a list.
- Add a unit test for both str and list inputs on helpers that take category collections.
When it happens
Trigger: Calling format_memory_prompt(memory_data, guaranteed_categories="preferences") instead of ["preferences"]. Any single-string argument that should be an iterable of category names triggers it.
Common situations: Quick script or test passing a single category as a string; refactor that replaced a list variable with a scalar; dataclass field typed str instead of list[str] feeding the helper.
Related errors
- Missing or empty 'messages' key in {path}
- chat prompt template not found: {name} (searched: {searched}
- retrieval scope userId must be a string or null
- retrieval scope agentName must be a string or null
- retrieval fact.id must be a non-empty string
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/04eb2e7dede4f88c.
Report an issue: GitHub.