bytedance/deer-flow · critical · MemoryStorageCorruption
Legacy {section} summary migration conflict in {legacy_path}
Error message
Legacy {section} summary migration conflict in {legacy_path}; the legacy file was kept What it means
Thrown while migrating a legacy summary file into canonical storage: both the canonical section and the legacy section contain meaningful (non-empty) data and they differ, so the migrator cannot tell which copy is authoritative. Adopting the legacy section would silently overwrite live data, so it refuses and keeps the legacy file on disk. This is a MemoryStorageCorruption signal requiring operator judgment, not a bug to paper over.
Source
Thrown at backend/packages/harness/deerflow/agents/memory/backends/deermem/deermem/core/storage.py:119
}
def _has_meaningful_data(value: Any) -> bool:
"""Return whether a legacy summary value contains anything worth preserving."""
if isinstance(value, dict):
return any(_has_meaningful_data(item) for item in value.values())
if isinstance(value, (list, tuple, set)):
return any(_has_meaningful_data(item) for item in value)
return value not in (None, "", False)
def _merge_legacy_summary_section(*, canonical: Any, legacy: Any, section: str, legacy_path: Path) -> Any:
"""Adopt a legacy section only when doing so cannot overwrite live data."""
if canonical == legacy or not _has_meaningful_data(legacy):
return copy.deepcopy(canonical)
if not _has_meaningful_data(canonical):
return copy.deepcopy(legacy)
raise MemoryStorageCorruption(f"Legacy {section} summary migration conflict in {legacy_path}; the legacy file was kept")
def _scope_dict(user_id: str | None, agent_name: str | None) -> dict[str, str | None]:
return {"userId": user_id, "agentName": agent_name}
def _content_hash(raw: bytes) -> str:
return f"sha256:{hashlib.sha256(raw).hexdigest()}"
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
View on GitHub (pinned to 1dd6ba1acb)
Solutions
- Inspect both files named in the message: keep the section whose content is correct/current, and manually delete or empty the other, then retry the operation that triggered migration.
- If the legacy file is the good copy, remove the conflicting canonical section so it has no meaningful data and the legacy section is adopted cleanly.
- As a last resort, back up the whole memory directory and re-initialize the store, restoring facts manually.
Defensive patterns
Strategy: try-catch
Validate before calling
# before triggering migration, detect the conflict yourself
def legacy_conflicts(canonical: dict, legacy: dict) -> bool:
return any(
canonical.get(k) != legacy.get(k) and canonical.get(k) and legacy.get(k)
for k in set(canonical) | set(legacy)
) Try / catch
try:
store.upgrade_legacy()
except MemoryStorageCorruption as exc:
if "migration conflict" in str(exc):
# both file paths are in the message; halt and page a human
# do NOT delete either file automatically
raise Prevention
- Run only one DeerFlow version (and one process) against a memory directory at a time.
- Never hand-edit summary JSON while a migration is pending.
- Back up the memory directory before version upgrades.
When it happens
Trigger: A v1 legacy summary file exists beside a canonical store that was already written by v2 code (e.g. the store was partially migrated, then a new session wrote fresh summary data), and the two sections diverge. Also occurs when users hand-edit either JSON file or restore one file from an older backup.
Common situations: Downgrade/upgrade cycles between DeerFlow versions; restoring canonical files from a backup while keeping the newer legacy file; two machines or containers writing to the same memory directory.
Related errors
- Existing migration backup {backup_path} differs from source
- Failed to create durable migration backup {backup_path}: {ex
- Stored fact {normalized['id']!r} has an invalid revision
- {label} path escapes the user memory directory: {relative!r}
- Missing or empty 'messages' key in {path}
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/22bd23cc25803ac7.
Report an issue: GitHub.