bytedance/deer-flow · error · ValueError
memory_data.facts
Error message
memory_data.facts
What it means
Thrown by DeerMem's memory-import path when the incoming 'facts' section of memory_data is not a JSON list of objects. Before diffing against current facts, the updater validates that memory_data['facts'] is a list whose entries are all dicts, and refuses anything else. This is a caller-input contract: imported memory must round-trip the shape that get_memory_data() produces.
Source
Thrown at backend/packages/harness/deerflow/agents/memory/backends/deermem/deermem/core/updater.py:833
raise ValueError("memory_data")
memory_data = copy.deepcopy(memory_data)
empty = create_empty_memory()
for section in ("user", "history"):
incoming_section = memory_data.get(section, {})
if not isinstance(incoming_section, dict):
raise ValueError(f"memory_data.{section}")
complete_section = copy.deepcopy(empty[section])
for key, value in incoming_section.items():
if key in complete_section and isinstance(complete_section[key], dict) and isinstance(value, dict):
complete_section[key].update(copy.deepcopy(value))
else:
complete_section[key] = copy.deepcopy(value)
memory_data[section] = complete_section
if agent_name is not None and getattr(type(self._storage), "apply_changes", None) is not MemoryStorage.apply_changes:
current = self.get_memory_data(agent_name, user_id=user_id)
incoming_facts = copy.deepcopy(memory_data.get("facts", []))
if not isinstance(incoming_facts, list) or any(not isinstance(fact, dict) for fact in incoming_facts):
raise ValueError("memory_data.facts")
for fact in incoming_facts:
fact["id"] = str(fact.get("id") or f"fact_{uuid.uuid4().hex}")
fact["confidence"] = _coerce_source_confidence(fact)
current_by_id = {str(fact.get("id")): fact for fact in current.get("facts", []) if isinstance(fact, dict)}
incoming_ids = {str(fact.get("id")) for fact in incoming_facts}
self._storage.apply_changes(
{
"upserts": incoming_facts,
"upsertRevisions": {str(fact.get("id")): (int(current_by_id[str(fact.get("id"))].get("revision") or 1) if str(fact.get("id")) in current_by_id else None) for fact in incoming_facts},
"deletes": [fact_id for fact_id in current_by_id if fact_id not in incoming_ids],
"deleteRevisions": {fact_id: int(fact.get("revision") or 1) for fact_id, fact in current_by_id.items() if fact_id not in incoming_ids},
"summaries": {"user": copy.deepcopy(memory_data.get("user", {})), "history": copy.deepcopy(memory_data.get("history", {}))},
},
agent_name=agent_name,
user_id=user_id,
expected_manifest_revision=int(current.get("revision") or 0),
)
return self._storage.load(agent_name, user_id=user_id)View on GitHub (pinned to 1dd6ba1acb)
Solutions
- Pass facts as a list of dicts: memory_data['facts'] = [{'id': ..., 'content': ..., 'category': ..., 'confidence': ...}, ...]
- If importing from an export, verify the export was produced by get_memory_data() for the same backend version
- Validate the payload shape before calling import (see validationCode)
Example fix
// before
await memory.import_memory_data({
agent_name: "researcher",
memory_data: { facts: { content: "likes tea" } },
});
// after
await memory.import_memory_data({
agent_name: "researcher",
memory_data: { facts: [{ content: "likes tea" }] },
}); Defensive patterns
Strategy: validation
Validate before calling
def is_valid_import_payload(memory_data: dict) -> bool:
facts = memory_data.get("facts", [])
return isinstance(facts, list) and all(isinstance(f, dict) for f in facts) Try / catch
try:
memory.import_memory_data(memory_data, agent_name=agent)
except ValueError as e:
if str(e) == "memory_data.facts":
raise HTTPException(400, "facts must be a list of objects")
raise Prevention
- Only import payloads exported by get_memory_data() from the same backend version
- Validate the facts shape at your API boundary before forwarding to the backend
When it happens
Trigger: Calling the memory import API (import_memory_data / the tool that feeds it) with memory_data={'facts': {'id': 'x'}} (dict instead of list), facts being a list of strings/numbers, facts being null, or facts entries like "some fact" instead of {'content': ...} dicts.
Common situations: Hand-written import payloads, exporting from another memory system and mapping 'facts' to a single object, JSON schema drift after upgrading the backend, or a frontend form posting facts as a plain string array.
Related errors
- memory_data
- memory_data.{section}
- retrieval fact.id must be a non-empty string
- retrieval fact.content must be a non-empty string
- unsupported FTS5 retrieval mode: {mode}
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/30a1e6e3909fa887.
Report an issue: GitHub.