bytedance/deer-flow · error · OSError

Failed to save imported memory data

Error message

Failed to save imported memory data

What it means

Raised when the legacy single-file storage path of DeerMem's import returns falsy from storage.save(memory_data, agent_name, user_id). This branch only runs when the storage backend does not override MemoryStorage.apply_changes (i.e. the legacy file backend). A False return means the atomic write/rename to the memory file failed or the expected revision did not match.

Source

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

            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)
        if agent_name is None:
            memory_data["facts"] = []
        if not self._storage.save(memory_data, agent_name, user_id=user_id):
            raise OSError("Failed to save imported memory data")
        return self._storage.load(agent_name, user_id=user_id)

    def clear_memory_data(self, agent_name: str | None = None, *, user_id: str | None = None) -> dict[str, Any]:
        """Clear one selected agent's facts without resetting shared summaries."""
        if agent_name is not None and getattr(type(self._storage), "apply_changes", None) is not MemoryStorage.apply_changes:
            for attempt in range(3):
                current = self.get_memory_data(agent_name, user_id=user_id) if attempt == 0 else self.reload_memory_data(agent_name, user_id=user_id)
                facts = [fact for fact in current.get("facts", []) if isinstance(fact, dict)]
                try:
                    self._storage.apply_changes(
                        {
                            "deletes": [str(fact.get("id")) for fact in facts],
                            "deleteRevisions": {str(fact.get("id")): int(fact.get("revision") or 1) for fact in facts},
                        },
                        agent_name=agent_name,
                        user_id=user_id,
                        expected_manifest_revision=int(current.get("revision") or 0),
                    )

View on GitHub (pinned to 1dd6ba1acb)

Solutions

  1. Check filesystem permissions on the DeerMem storage directory (the process user must own or be able to write it)
  2. Retry the import once the concurrent write finishes; the legacy path does not auto-retry revision conflicts
  3. Switch to a storage backend that implements apply_changes (which handles revision conflicts internally) if concurrency is expected
  4. Verify disk space on the volume holding the memory files
Defensive patterns

Strategy: retry

Validate before calling

import os

def storage_writable(path: str) -> bool:
    return os.access(os.path.dirname(path) or ".", os.W_OK)

Try / catch

try:
    memory.import_memory_data(memory_data, agent_name=agent)
except OSError:
    # transient revision race or I/O failure; single retry after re-read
    time.sleep(0.2)
    memory.import_memory_data(memory_data, agent_name=agent)

Prevention

When it happens

Trigger: import_memory_data on the legacy file storage while the memory file is unwritable (permissions, read-only mount, disk full), the memory directory was deleted mid-run, or a concurrent writer bumped the file revision so the conditional save refused.

Common situations: Running the Gateway as a user without write access to the memory data directory, a container with a read-only volume mounted over the memory path, or two concurrent imports/updates racing on the same agent's file.

Related errors


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