{"record":{"id":"1c2d80a6bb950711","repo":"bytedance/deer-flow","slug":"fact-id","errorCode":null,"errorMessage":"{fact_id}","messagePattern":"\\{fact_id\\}","errorType":"validation","errorClass":"KeyError","httpStatus":null,"severity":"error","filePath":"backend/packages/harness/deerflow/agents/memory/backends/deermem/deermem/core/updater.py","lineNumber":1001,"sourceCode":"            updated_memory = dict(memory_data)\n            updated_memory[\"facts\"] = _trim_facts_to_max([*memory_data.get(\"facts\", []), copy.deepcopy(candidate)], self._config.max_facts)\n            if self._save_memory_to_file(updated_memory, agent_name, user_id=user_id, expected_revision=int(memory_data.get(\"revision\") or 0)):\n                # If the cap evicted the just-added (lower-confidence) fact,\n                # signal via None so callers don't report a dangling id as\n                # \"added\".\n                stored = any(f.get(\"id\") == fact_id for f in updated_memory[\"facts\"])\n                return updated_memory, (fact_id if stored else None)\n            logger.info(\"Retrying capped fact creation from a fresh snapshot after a revision conflict\")\n        raise OSError(\"Failed to save memory data after creating fact\")\n\n    def delete_memory_fact(self, fact_id: str, agent_name: str | None = None, *, user_id: str | None = None) -> dict[str, Any]:\n        \"\"\"Delete a fact by its id and persist the updated memory data.\"\"\"\n        if agent_name is None:\n            raise ValueError(\"agent_name\")\n        if getattr(type(self._storage), \"apply_changes\", None) is not MemoryStorage.apply_changes and hasattr(self._storage, \"get_fact\"):\n            deleted = self._storage.get_fact(fact_id, agent_name=agent_name, user_id=user_id)\n            if deleted is None:\n                raise KeyError(fact_id)\n            global_memory = self.get_memory_data(user_id=user_id)\n            self._storage.apply_changes(\n                {\"deletes\": [fact_id], \"deleteRevisions\": {fact_id: int(deleted.get(\"revision\") or 1)}},\n                agent_name=agent_name,\n                user_id=user_id,\n                expected_manifest_revision=int(global_memory.get(\"revision\") or 0),\n                allow_manifest_rebase=True,\n            )\n            return self.get_memory_data(agent_name, user_id=user_id)\n        memory_data = self.get_memory_data(agent_name, user_id=user_id)\n        facts = memory_data.get(\"facts\", [])\n        updated_facts = [fact for fact in facts if fact.get(\"id\") != fact_id]\n        if len(updated_facts) == len(facts):\n            raise KeyError(fact_id)\n        deleted = next(fact for fact in facts if fact.get(\"id\") == fact_id)\n        if getattr(type(self._storage), \"apply_changes\", None) is not MemoryStorage.apply_changes:\n            self._storage.apply_changes(\n                {\"deletes\": [fact_id], \"deleteRevisions\": {fact_id: int(deleted.get(\"revision\") or 1)}},","sourceCodeStart":983,"sourceCodeEnd":1019,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/backend/packages/harness/deerflow/agents/memory/backends/deermem/deermem/core/updater.py#L983-L1019","documentation":"On storage backends that implement apply_changes and get_fact, delete_memory_fact first fetches the fact by id; if get_fact returns None the id is unknown and KeyError(fact_id) is raised. This checks existence before issuing the delete changeset so a bogus id cannot silently succeed.","triggerScenarios":"Deleting a fact id that was already deleted, an id from a different user or agent bucket, or a truncated/typo'd id string (ids look like 'fact_ab12cd34').","commonSituations":"Stale UI list after another session deleted the fact, double-submit of a delete button, or ids carried across user contexts.","solutions":["Handle KeyError as a 404/idempotent 'already gone' case instead of crashing","Re-fetch the fact list before showing delete actions so stale ids are not offered","Guard deletes with an existence check against get_memory_data()"],"exampleFix":"// before\nmemory.delete_memory_fact(fid, agent_name=agent)\n// after\ntry:\n    memory.delete_memory_fact(fid, agent_name=agent)\nexcept KeyError:\n    pass  # already deleted; treat as success for idempotent UI","handlingStrategy":"try-catch","validationCode":"facts = memory.get_memory_data(agent_name=agent).get(\"facts\", [])\nif not any(f.get(\"id\") == fact_id for f in facts):\n    raise HTTPException(404, \"fact not found\")","typeGuard":null,"tryCatchPattern":"try:\n    memory.delete_memory_fact(fact_id, agent_name=agent)\nexcept KeyError:\n    pass  # idempotent: already deleted","preventionTips":["Refresh the fact list before offering delete actions","Treat delete as idempotent in the UI (absorbs double-submits)"],"tags":["memory","deermem","not-found","delete"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}