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
- Pass a concrete agent_name, e.g. create_memory_fact('likes tea', 'context', 0.8, 'researcher')
- If wrapping the call, default the missing parameter at your boundary: agent_name or DEFAULT_AGENT
- 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
- Make agent_name a required field in every tool/HTTP schema that reaches memory writes
- Unit-test the missing-agent_name path so the 400 mapping stays in place
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
- retrieval fact.id must be a non-empty string
- retrieval fact.content must be a non-empty string
- unsupported FTS5 retrieval mode: {mode}
- retrieval category filter must be a string
- fact.category must be a string
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/2598e6c0bc7a9538.
Report an issue: GitHub.