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

  1. Pass a list/tuple of category strings: guaranteed_categories=["preferences"].
  2. If the variable may be either, normalize: cats = [cats] if isinstance(cats, str) else cats.
  3. 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

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


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