bytedance/deer-flow · error · ValueError

agent_name

Error message

agent_name

What it means

create_memory_fact requires an explicit agent_name; passing None (the default) raises ValueError('agent_name') immediately. DeerMem stores facts in per-agent buckets even on shared backends, so the code refuses to guess a bucket.

Source

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

        misreport a storage cap on backends that normalize differently.

        The new fact is then trimmed by :func:`_trim_facts_to_max` (highest-
        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):

View on GitHub (pinned to 1dd6ba1acb)

Solutions

  1. Pass a concrete agent_name, e.g. create_memory_fact('likes tea', 'context', 0.8, 'researcher')
  2. If wrapping the call, default the missing parameter at your boundary: agent_name or DEFAULT_AGENT
  3. Fail fast at your API surface with a 400 instead of letting ValueError escape as a 500

Example fix

// before
memory.create_memory_fact(content="likes tea", category="context")
// after
memory.create_memory_fact(content="likes tea", category="context", agent_name="researcher")
Defensive patterns

Strategy: validation

Validate before calling

if not agent_name:
    raise HTTPException(400, "agent_name is required")
_, fact_id = memory.create_memory_fact(content, agent_name=agent_name)

Type guard

def has_agent(agent: str | None) -> TypeGuard[str]:
    return isinstance(agent, str) and bool(agent)

Try / catch

try:
    memory.create_memory_fact(content, agent_name=agent)
except ValueError as e:
    if str(e) == "agent_name":
        raise HTTPException(400, "agent_name is required")
    raise

Prevention

When it happens

Trigger: Calling create_memory_fact(content, category, confidence) without the positional agent_name argument, or passing agent_name=None explicitly (e.g. forwarding an optional CLI/tool parameter that was never filled in).

Common situations: A wrapper tool exposes an optional agent parameter and forwards it verbatim; migrations from an older API where agent_name was not required; tests that call the factory method with default args.

Related errors


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