{"record":{"id":"43af5d0fd3666bf9","repo":"bytedance/deer-flow","slug":"existing-migration-backup-backup-path-differs-fr","errorCode":null,"errorMessage":"Existing migration backup {backup_path} differs from source {source_path}; the original backup was kept and migration was stopped","messagePattern":"Existing migration backup (.+?) differs from source (.+?); the original backup was kept and migration was stopped","errorType":"exception","errorClass":"MemoryStorageCorruption","httpStatus":null,"severity":"critical","filePath":"backend/packages/harness/deerflow/agents/memory/backends/deermem/deermem/core/storage.py","lineNumber":146,"sourceCode":"\n\ndef _file_signature(path: Path) -> tuple[int, int] | None:\n    \"\"\"Use nanosecond mtime plus size so cache validation is not mtime-only.\"\"\"\n    try:\n        stat = path.stat()\n        return (stat.st_mtime_ns, stat.st_size)\n    except OSError:\n        return None\n\n\ndef _ensure_migration_backup(source_path: Path) -> Path:\n    \"\"\"Durably preserve one immutable pre-migration JSON source beside it.\"\"\"\n    backup_path = source_path.with_name(f\"{source_path.name}.v1.bak\")\n    try:\n        source_bytes = source_path.read_bytes()\n        if backup_path.exists():\n            if backup_path.read_bytes() != source_bytes:\n                raise MemoryStorageCorruption(f\"Existing migration backup {backup_path} differs from source {source_path}; the original backup was kept and migration was stopped\")\n            return backup_path\n        _atomic_write(backup_path, source_bytes)\n        return backup_path\n    except MemoryStorageCorruption:\n        raise\n    except OSError as exc:\n        raise OSError(f\"Failed to create durable migration backup {backup_path}: {exc}\") from exc\n\n\ndef _normalize_category(fact: dict[str, Any]) -> None:\n    raw_category = fact.get(\"category\", \"context\")\n    if not isinstance(raw_category, str):\n        raise ValueError(\"fact.category must be a string\")\n    category = raw_category or \"context\"\n    if category not in CORE_CATEGORIES:\n        fact.setdefault(\"categoryExtension\", category)\n        fact[\"category\"] = \"other\"\n","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/backend/packages/harness/deerflow/agents/memory/backends/deermem/deermem/core/storage.py#L128-L164","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"from pathlib import Path\n\ndef backup_consistent(source: Path) -> bool:\n    bak = source.with_name(source.name + \".v1.bak\")\n    return not bak.exists() or bak.read_bytes() == source.read_bytes()","typeGuard":null,"tryCatchPattern":"try:\n    store.migrate()\nexcept MemoryStorageCorruption as exc:\n    if \"differs from source\" in str(exc):\n        # decide which of memory.json / memory.json.v1.bak is authoritative,\n        # reconcile by hand, delete the stale one, retry\n        raise","preventionTips":["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."],"tags":["deermem","memory","migration","backup"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}