bytedance/deer-flow · error · KeyError
{fact_id}
Error message
{fact_id} What it means
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.
Source
Thrown at backend/packages/harness/deerflow/agents/memory/backends/deermem/deermem/core/updater.py:1001
updated_memory = dict(memory_data)
updated_memory["facts"] = _trim_facts_to_max([*memory_data.get("facts", []), copy.deepcopy(candidate)], self._config.max_facts)
if self._save_memory_to_file(updated_memory, agent_name, user_id=user_id, expected_revision=int(memory_data.get("revision") or 0)):
# If the cap evicted the just-added (lower-confidence) fact,
# signal via None so callers don't report a dangling id as
# "added".
stored = any(f.get("id") == fact_id for f in updated_memory["facts"])
return updated_memory, (fact_id if stored else None)
logger.info("Retrying capped fact creation from a fresh snapshot after a revision conflict")
raise OSError("Failed to save memory data after creating fact")
def delete_memory_fact(self, fact_id: str, agent_name: str | None = None, *, user_id: str | None = None) -> dict[str, Any]:
"""Delete a fact by its id and persist the updated memory data."""
if agent_name is None:
raise ValueError("agent_name")
if getattr(type(self._storage), "apply_changes", None) is not MemoryStorage.apply_changes and hasattr(self._storage, "get_fact"):
deleted = self._storage.get_fact(fact_id, agent_name=agent_name, user_id=user_id)
if deleted is None:
raise KeyError(fact_id)
global_memory = self.get_memory_data(user_id=user_id)
self._storage.apply_changes(
{"deletes": [fact_id], "deleteRevisions": {fact_id: int(deleted.get("revision") or 1)}},
agent_name=agent_name,
user_id=user_id,
expected_manifest_revision=int(global_memory.get("revision") or 0),
allow_manifest_rebase=True,
)
return self.get_memory_data(agent_name, user_id=user_id)
memory_data = self.get_memory_data(agent_name, user_id=user_id)
facts = memory_data.get("facts", [])
updated_facts = [fact for fact in facts if fact.get("id") != fact_id]
if len(updated_facts) == len(facts):
raise KeyError(fact_id)
deleted = next(fact for fact in facts if fact.get("id") == fact_id)
if getattr(type(self._storage), "apply_changes", None) is not MemoryStorage.apply_changes:
self._storage.apply_changes(
{"deletes": [fact_id], "deleteRevisions": {fact_id: int(deleted.get("revision") or 1)}},View on GitHub (pinned to 1dd6ba1acb)
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()
Example fix
// before
memory.delete_memory_fact(fid, agent_name=agent)
// after
try:
memory.delete_memory_fact(fid, agent_name=agent)
except KeyError:
pass # already deleted; treat as success for idempotent UI Defensive patterns
Strategy: try-catch
Validate before calling
facts = memory.get_memory_data(agent_name=agent).get("facts", [])
if not any(f.get("id") == fact_id for f in facts):
raise HTTPException(404, "fact not found") Try / catch
try:
memory.delete_memory_fact(fact_id, agent_name=agent)
except KeyError:
pass # idempotent: already deleted Prevention
- Refresh the fact list before offering delete actions
- Treat delete as idempotent in the UI (absorbs double-submits)
When it happens
Trigger: 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').
Common situations: Stale UI list after another session deleted the fact, double-submit of a delete button, or ids carried across user contexts.
Related errors
- Failed to save memory data after deleting fact '{fact_id}'
- Missing or empty 'messages' key in {path}
- chat prompt template not found: {name} (searched: {searched}
- guaranteed_categories must be an iterable of strings, not a
- memory update queue is full (depth {len(self._items)} >= {ma
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/1c2d80a6bb950711.
Report an issue: GitHub.