bytedance/deer-flow · critical · MemoryStorageCorruption
Stored fact {normalized['id']!r} has an invalid revision
Error message
Stored fact {normalized['id']!r} has an invalid revision What it means
While rebasing an update onto an existing stored fact, the stored fact's own 'revision' field is not a valid int >= 1, so the optimistic-concurrency comparison cannot proceed. Raised as MemoryStorageCorruption: the on-disk store itself is damaged, independent of the incoming payload.
Source
Thrown at backend/packages/harness/deerflow/agents/memory/backends/deermem/deermem/core/storage.py:236
normalized["source"] = {"type": "conversation", "threadId": source}
elif not isinstance(source, dict):
normalized["source"] = {"type": "unknown", "threadId": None}
else:
normalized["source"].setdefault("type", "unknown")
if not isinstance(normalized["source"].get("type"), str):
raise ValueError("fact.source.type must be a string")
if normalized["source"].get("threadId") is not None and not isinstance(normalized["source"].get("threadId"), str):
raise ValueError("fact.source.threadId must be a string or null")
normalized["title"] = _fact_title(normalized)
now = utc_now_iso_z()
if existing is None:
normalized.setdefault("createdAt", now)
normalized.setdefault("updatedAt", normalized["createdAt"])
normalized["revision"] = revision
else:
existing_revision = existing.get("revision")
if not isinstance(existing_revision, int) or existing_revision < 1:
raise MemoryStorageCorruption(f"Stored fact {normalized['id']!r} has an invalid revision")
if revision != existing_revision:
raise MemoryFactRevisionConflict(f"Expected fact {normalized['id']!r} revision {revision}, found {existing_revision}")
normalized["createdAt"] = existing.get("createdAt") or normalized.get("createdAt") or now
comparison_keys = {"revision", "updatedAt"}
incoming_material = {key: value for key, value in normalized.items() if key not in comparison_keys}
existing_material = {key: value for key, value in existing.items() if key not in comparison_keys}
if incoming_material == existing_material:
normalized["revision"] = existing_revision
normalized["updatedAt"] = existing.get("updatedAt") or normalized["createdAt"]
else:
normalized["revision"] = existing_revision + 1
normalized["updatedAt"] = now
if not isinstance(normalized.get("createdAt"), str) or not isinstance(normalized.get("updatedAt"), str):
raise ValueError("fact.createdAt and fact.updatedAt must be strings")
if normalized["consolidatedFrom"]:
normalized.setdefault("consolidatedAt", normalized["updatedAt"])
return normalized
View on GitHub (pinned to 1dd6ba1acb)
Solutions
- Locate the fact file named by the id in the message and inspect its revision field.
- Repair the stored fact by setting revision to a valid positive int (1 if unsure) after backing up the file, or delete the corrupted record so it can be recreated.
- Audit the whole memory directory for other malformed revisions and re-run the store's consistency/repair tooling if available.
Defensive patterns
Strategy: try-catch
Validate before calling
rev = existing_record.get("revision")
if isinstance(rev, bool) or not isinstance(rev, int) or rev < 1:
quarantine(existing_record) # do not attempt rebase on damaged data Type guard
def stored_fact_revision_ok(record: dict) -> bool:
r = record.get("revision")
return not isinstance(r, bool) and isinstance(r, int) and r >= 1 Try / catch
try:
store.save(fact)
except MemoryStorageCorruption as exc:
if "invalid revision" in str(exc):
# repair: back up, set stored revision to 1 (or delete record), retry once
raise Prevention
- Never hand-edit revision fields in stored JSON.
- Keep memory directories out of sync-tool conflict merges.
- Back up before bulk edits so damaged records can be restored.
When it happens
Trigger: An update to a fact whose stored JSON has revision='2', revision=0, or a bad value set by a prior hand-edit or a partially failed migration. Distinguishing feature vs error 496: here the STORED value is malformed, not merely different.
Common situations: Hand-edited memory JSON; older versions writing a different revision shape; partial writes from crashes; external sync tools merging JSON badly.
Related errors
- Legacy {section} summary migration conflict in {legacy_path}
- fact.revision must be an integer >= 1
- Expected fact {normalized['id']!r} revision {revision}, foun
- {label} path escapes the user memory directory: {relative!r}
- change_set.deleteRevisions must be an object
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/9bdca864fcb57ed5.
Report an issue: GitHub.