bytedance/deer-flow · error · ValueError

memory_data.{section}

Error message

memory_data.{section}

What it means

During import, each summaries section memory_data['user'] and memory_data['history'] must itself be a dict (missing keys default to {}). The f-string message names the offending section ('memory_data.user' or 'memory_data.history'). The check runs per section before merging into the empty template, so nothing is persisted when it fires.

Source

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

    def get_memory_data(self, agent_name: str | None = None, *, user_id: str | None = None) -> dict[str, Any]:
        """Get the current memory data via the injected storage."""
        return self._storage.load(agent_name, user_id=user_id)

    def reload_memory_data(self, agent_name: str | None = None, *, user_id: str | None = None) -> dict[str, Any]:
        """Reload memory data via the injected storage."""
        return self._storage.reload(agent_name, user_id=user_id)

    def import_memory_data(self, memory_data: dict[str, Any], agent_name: str | None = None, *, user_id: str | None = None) -> dict[str, Any]:
        """Persist imported memory data via the injected storage."""
        if not isinstance(memory_data, dict):
            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(

View on GitHub (pinned to 1dd6ba1acb)

Solutions

  1. Make each section an object: {'user': {'name': ...}, 'history': {'summary': ...}}.
  2. Convert legacy list-shaped history into the dict shape at import time (e.g. {'messages': [...]}) before calling import_memory_data.
  3. Validate the upload with a pydantic model declaring user/history as dict fields.

Example fix

# before
updater.import_memory_data({"user": "likes tea", "history": []}, agent_name=a)

# after
updater.import_memory_data({"user": {"preferences": "likes tea"}, "history": {"messages": []}}, agent_name=a)
Defensive patterns

Strategy: type-guard

Validate before calling

for section in ("user", "history"):
    value = memory_data.get(section, {})
    if not isinstance(value, dict):
        raise HTTPException(400, f"memory_data.{section} must be an object")

Type guard

def has_valid_summary_sections(doc: object) -> TypeGuard[dict[str, Any]]:
    return isinstance(doc, dict) and all(
        isinstance(doc.get(section, {}), dict) for section in ("user", "history")
    )

Prevention

When it happens

Trigger: import_memory_data({'user': 'friendly assistant', ...}) where the section is a plain string; {'history': [...messages...]} as a list; JSON exports where these sections were flattened into arrays or strings.

Common situations: Older export formats storing history as a message list; hand-written import files; LLM-generated memory documents putting prose in 'user' instead of a structured profile dict.

Related errors


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