{"record":{"id":"c657b1bce2b7bc08","repo":"bytedance/deer-flow","slug":"failed-to-update-global-memory-summaries","errorCode":null,"errorMessage":"Failed to update global memory summaries","messagePattern":"Failed to update global memory summaries","errorType":"exception","errorClass":"MemoryStorageError","httpStatus":null,"severity":"error","filePath":"backend/packages/harness/deerflow/agents/memory/backends/deermem/deermem/core/storage.py","lineNumber":1379,"sourceCode":"        agent_name: str | None = None,\n    ) -> dict[str, Any]:\n        document = self.load(agent_name, user_id=user_id)\n        return {\"user\": copy.deepcopy(document.get(\"user\", {})), \"history\": copy.deepcopy(document.get(\"history\", {})), \"revision\": document.get(\"revision\", 0)}\n\n    def update_summaries(\n        self,\n        summaries: dict[str, Any],\n        *,\n        user_id: str | None = None,\n        agent_name: str | None = None,\n        expected_revision: int | None = None,\n    ) -> dict[str, Any]:\n        # Summaries are always user-global, never agent-specific.\n        document = self.load(user_id=user_id)\n        document.update({key: copy.deepcopy(value) for key, value in summaries.items() if key in {\"user\", \"history\"}})\n        expected = int(document.get(\"revision\") or 0) if expected_revision is None else expected_revision\n        if not self.save(document, user_id=user_id, expected_revision=expected):\n            raise MemoryStorageError(\"Failed to update global memory summaries\")\n        return self.reload(user_id=user_id)\n\n    def notify_fact_upsert(self, fact: dict[str, Any], *, path: str = \"\") -> bool:\n        if self._retrieval is None:\n            return False\n        scope = fact.get(\"scope\") if isinstance(fact.get(\"scope\"), dict) else {}\n        self._retrieval.upsert(copy.deepcopy(fact), scope=copy.deepcopy(scope), path=path)\n        return True\n\n    def notify_fact_remove(self, fact_id: str, *, scope: dict[str, str | None]) -> bool:\n        if self._retrieval is None:\n            return False\n        self._retrieval.remove(fact_id, scope=copy.deepcopy(scope))\n        return True\n\n    def search_facts(\n        self,\n        query: str,","sourceCodeStart":1361,"sourceCodeEnd":1397,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/backend/packages/harness/deerflow/agents/memory/backends/deermem/deermem/core/storage.py#L1361-L1397","documentation":"update_summaries() does load -> merge -> save(document, expected_revision) and raises MemoryStorageError when save() returns False. save() returns False on optimistic-concurrency failure (revision mismatch) or other non-exception commit rejections, meaning another writer changed the global memory document between the load and the save. Summaries are always user-global, never agent-specific, so the contention is on the user-level file.","triggerScenarios":"Two threads/processes updating user summaries concurrently (e.g. two agents finishing turns for the same user at once); calling update_summaries repeatedly in a loop without reloading between attempts; passing an expected_revision that is already stale.","commonSituations":"Multi-agent workflows sharing one user_id; a memory updater racing with a fact save that also bumps the document revision; long-running processes holding an old revision across other writes.","solutions":["Retry the whole operation: catch MemoryStorageError, reload via storage.reload(user_id=...), re-apply the summary merge, save again (bounded attempts).","Serialize summary updates per user_id with a lock in your service layer if retries are undesirable.","Update summaries through a single owner (one coordinator) instead of many concurrent writers."],"exampleFix":"# before\nstorage.update_summaries({\"user\": summary}, user_id=user_id)\n\n# after\nfor attempt in range(3):\n    try:\n        storage.update_summaries({\"user\": summary}, user_id=user_id)\n        break\n    except MemoryStorageError:\n        if attempt == 2:\n            raise\n        time.sleep(0.1 * (attempt + 1))","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"last_exc = None\nfor attempt in range(3):\n    try:\n        storage.update_summaries(summaries, user_id=user_id)\n        break\n    except MemoryStorageError as exc:\n        last_exc = exc\n        storage.reload(user_id=user_id)  # refresh revision before retry\nelse:\n    raise last_exc","preventionTips":["Serialize per-user summary updates behind a lock if contention is frequent.","Keep the load-merge-save window short; do expensive LLM work before loading.","Route all summary writes for a user through a single coordinator component."],"tags":["memory","concurrency","optimistic-locking","deermem"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}