bytedance/deer-flow · error · ValueError

content

Error message

content

What it means

create_memory_fact strips the content argument and raises ValueError('content') when the result is empty. Whitespace-only strings are treated as absent content, because a fact with no text is unstoreable and would also collide on the normalized-content duplicate key.

Source

Thrown at backend/packages/harness/deerflow/agents/memory/backends/deermem/deermem/core/updater.py:928

        confidence wins, confidence coerced). If the cap evicts the just-added
        (lower-confidence) fact, ``fact_id`` is ``None`` so callers report
        "not stored - cap reached" instead of a dangling id with a false
        "added" status. This restores both the max_facts cap and the post-trim
        existence check (upstream's ``create_memory_fact_with_created_fact``),
        which the vendored copy had dropped together to avoid the dangling id.

        Duplicate rejection is enforced here (not only by callers): the
        candidate's normalized content key is checked against the fresh
        memory snapshot inside the revision-conflict retry loop of both
        storage paths (apply_changes and legacy single-file save), so
        concurrent creators cannot both store the same content. Raises
        ``ValueError("Duplicate fact")`` on a normalized-content match.
        """
        if agent_name is None:
            raise ValueError("agent_name")
        normalized_content = content.strip()
        if not normalized_content:
            raise ValueError("content")
        normalized_category = category.strip() or "context"
        validated_confidence = _validate_confidence(confidence)
        candidate_key = _fact_content_key(normalized_content)
        now = utc_now_iso_z()
        fact_id = f"fact_{uuid.uuid4().hex[:8]}"
        candidate = {
            "id": fact_id,
            "content": normalized_content,
            "category": normalized_category,
            "confidence": validated_confidence,
            "createdAt": now,
            "source": "manual",
        }
        if getattr(type(self._storage), "apply_changes", None) is not MemoryStorage.apply_changes:
            for attempt in range(3):
                memory_data = self.get_memory_data(agent_name, user_id=user_id) if attempt == 0 else self.reload_memory_data(agent_name, user_id=user_id)
                # Duplicate rejection lives inside the conflict-retry loop so
                # it is re-evaluated against the fresh snapshot after every

View on GitHub (pinned to 1dd6ba1acb)

Solutions

  1. Validate and reject blank input before calling: if not content or not content.strip(): return error
  2. Trim input at the API boundary so downstream code sees normalized text
  3. If the value comes from an LLM, add a retry/check for empty extraction results

Example fix

// before
fact_id = memory.create_memory_fact(content=user_text, agent_name=agent).1
// after
if not user_text or not user_text.strip():
    raise HTTPException(400, "content must not be empty")
_, fact_id = memory.create_memory_fact(content=user_text.strip(), agent_name=agent)
Defensive patterns

Strategy: validation

Validate before calling

normalized = (content or "").strip()
if not normalized:
    raise HTTPException(400, "content must not be empty")

Type guard

def is_nonblank(value: str | None) -> TypeGuard[str]:
    return isinstance(value, str) and bool(value.strip())

Try / catch

try:
    memory.create_memory_fact(content, agent_name=agent)
except ValueError as e:
    if str(e) == "content":
        raise HTTPException(400, "content must not be empty")
    raise

Prevention

When it happens

Trigger: Calling create_memory_fact('') or create_memory_fact(' \n '), or forwarding user/LLM input that was never checked for blankness (empty form field, model returned empty string).

Common situations: Memory-add tool wired directly to a chat input with no required-field validation, or an extraction prompt whose model output collapses to whitespace.

Related errors


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