bytedance/deer-flow · critical · MemoryStorageCorruption
Existing migration backup {backup_path} differs from source
Error message
Existing migration backup {backup_path} differs from source {source_path}; the original backup was kept and migration was stopped What it means
During migration, deermem writes an immutable .v1.bak backup of the source JSON next to it. If a backup already exists but its bytes differ from the current source file, the migration halts (MemoryStorageCorruption) and the original backup is preserved. The check guarantees the backup always reflects the exact pre-migration state, so a mismatch means a previous migration attempt or external edit intervened.
Source
Thrown at backend/packages/harness/deerflow/agents/memory/backends/deermem/deermem/core/storage.py:146
def _file_signature(path: Path) -> tuple[int, int] | None:
"""Use nanosecond mtime plus size so cache validation is not mtime-only."""
try:
stat = path.stat()
return (stat.st_mtime_ns, stat.st_size)
except OSError:
return None
def _ensure_migration_backup(source_path: Path) -> Path:
"""Durably preserve one immutable pre-migration JSON source beside it."""
backup_path = source_path.with_name(f"{source_path.name}.v1.bak")
try:
source_bytes = source_path.read_bytes()
if backup_path.exists():
if backup_path.read_bytes() != source_bytes:
raise MemoryStorageCorruption(f"Existing migration backup {backup_path} differs from source {source_path}; the original backup was kept and migration was stopped")
return backup_path
_atomic_write(backup_path, source_bytes)
return backup_path
except MemoryStorageCorruption:
raise
except OSError as exc:
raise OSError(f"Failed to create durable migration backup {backup_path}: {exc}") from exc
def _normalize_category(fact: dict[str, Any]) -> None:
raw_category = fact.get("category", "context")
if not isinstance(raw_category, str):
raise ValueError("fact.category must be a string")
category = raw_category or "context"
if category not in CORE_CATEGORIES:
fact.setdefault("categoryExtension", category)
fact["category"] = "other"
View on GitHub (pinned to 1dd6ba1acb)
Solutions
- Compare memory.json with memory.json.v1.bak; if memory.json is the authoritative current state, delete the stale .v1.bak and retry so a fresh backup is written.
- If the .v1.bak is the true pre-migration snapshot you must keep, restore memory.json from it byte-for-byte before retrying.
- Ensure only one process migrates a given memory directory at a time (single instance, or stop the service before upgrading).
Defensive patterns
Strategy: try-catch
Validate before calling
from pathlib import Path
def backup_consistent(source: Path) -> bool:
bak = source.with_name(source.name + ".v1.bak")
return not bak.exists() or bak.read_bytes() == source.read_bytes() Try / catch
try:
store.migrate()
except MemoryStorageCorruption as exc:
if "differs from source" in str(exc):
# decide which of memory.json / memory.json.v1.bak is authoritative,
# reconcile by hand, delete the stale one, retry
raise Prevention
- Stop the service before upgrading so no writer touches memory.json between backup creation and migration.
- Exclude memory directories from file-sync conflict resolution or pause sync during migration.
- Do not run two instances against the same memory directory.
When it happens
Trigger: A first migration created memory.json.v1.bak; afterwards memory.json changed (second legacy write, partial migration, manual edit) and migration runs again, so the bytes no longer match. Also triggered by restoring memory.json from a different backup while keeping the old .v1.bak.
Common situations: Crashed or killed process midway through migration leaving divergent files; running two DeerFlow instances against one memory directory; sync tools (Dropbox/Nextcloud) restoring conflicting file versions.
Related errors
- Legacy {section} summary migration conflict in {legacy_path}
- Failed to create durable migration backup {backup_path}: {ex
- 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
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/43af5d0fd3666bf9.
Report an issue: GitHub.