bytedance/deer-flow · error · ValueError

agent_name is required to upsert a fact

Error message

agent_name is required to upsert a fact

What it means

MemoryStorage.upsert_fact() requires agent_name because it delegates to apply_changes, which writes to the agent-scoped fact file. The guard fires before the fact is deep-copied or an id is generated, so a failed call has no side effects and no id is consumed.

Source

Thrown at backend/packages/harness/deerflow/agents/memory/backends/deermem/deermem/core/storage.py:1323

            "complete": False,
            "version": memory_file.get("version", DOCUMENT_VERSION),
            "revision": memory_file.get("revision", 0),
            "lastUpdated": memory_file.get("lastUpdated", ""),
            "upsertedFacts": [copy.deepcopy(value) for action, value, _ in notifications if action == "upsert" and isinstance(value, dict)],
            "deletedFactIds": [str(value) for action, value, _ in notifications if action == "remove"],
        }

    def upsert_fact(
        self,
        fact: dict[str, Any],
        *,
        user_id: str | None = None,
        agent_name: str | None = None,
        expected_manifest_revision: int | None = None,
        expected_fact_revision: int | None = None,
    ) -> dict[str, Any]:
        if agent_name is None:
            raise ValueError("agent_name is required to upsert a fact")
        incoming = copy.deepcopy(fact)
        incoming["id"] = str(incoming.get("id") or f"fact_{uuid.uuid4().hex}")
        fact_id = incoming["id"]
        return self.apply_changes(
            {"upserts": [incoming], "upsertRevisions": {fact_id: expected_fact_revision}},
            user_id=user_id,
            agent_name=agent_name,
            expected_manifest_revision=expected_manifest_revision,
            allow_manifest_rebase=True,
        )

    def delete_fact(
        self,
        fact_id: str,
        *,
        user_id: str | None = None,
        agent_name: str | None = None,
        expected_manifest_revision: int | None = None,

View on GitHub (pinned to 1dd6ba1acb)

Solutions

  1. Thread the agent identity through: storage.upsert_fact(fact, agent_name=agent_name, user_id=user_id).
  2. If upserting outside any agent is a real use case, route it to your own scoped 'default' agent name rather than None.
  3. Make agent_name a required positional/keyword in your wrapper so it cannot be silently dropped.

Example fix

# before
def remember(fact: dict):
    storage.upsert_fact(fact, user_id=user_id)

# after
def remember(fact: dict, *, agent_name: str):
    storage.upsert_fact(fact, agent_name=agent_name, user_id=user_id)
Defensive patterns

Strategy: validation

Validate before calling

if agent_name is None:
    raise HTTPException(400, "agent_name required")
storage.upsert_fact(fact, agent_name=agent_name, user_id=user_id)

Prevention

When it happens

Trigger: storage.upsert_fact({'content': ...}) with agent_name omitted - typically a generic 'save fact' service method that only received the fact payload.

Common situations: A memory-extraction step running outside an agent context; refactor that lost the agent identity; defaulting agent_name=None in a wrapper signature and never overriding it.

Related errors


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