{"record":{"id":"b1c55ff4268100a5","repo":"libnyanpasu/clash-nyanpasu","slug":"cleanup-journals-have-conflicting-payloads","errorCode":null,"errorMessage":"cleanup journals have conflicting payloads","messagePattern":"cleanup journals have conflicting payloads","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/tauri/src/service/profile_file.rs","lineNumber":1360,"sourceCode":"            Err(error) => return Err(error).context(\"inspect pending cleanup journal\"),\n        };\n        let ready = match std::fs::symlink_metadata(&ready_path) {\n            Ok(metadata) if !is_symlink_or_reparse(&metadata) && metadata.is_file() => {\n                Some(Self::read_journal(&ready_path, operation_id)?)\n            }\n            Ok(_) => bail!(\"ready cleanup journal is not a regular file\"),\n            Err(error) if error.kind() == std::io::ErrorKind::NotFound => None,\n            Err(error) => return Err(error).context(\"inspect ready cleanup journal\"),\n        };\n        match (pending, ready) {\n            (None, None) => Ok(None),\n            (Some(journal), None) => Ok(Some((CleanupPhase::Pending, journal))),\n            (None, Some(journal)) => Ok(Some((CleanupPhase::Ready, journal))),\n            (Some(pending), Some(ready)) if pending == ready => {\n                Self::remove_private_regular(&pending_path)?;\n                Ok(Some((CleanupPhase::Ready, ready)))\n            }\n            (Some(_), Some(_)) => bail!(\"cleanup journals have conflicting payloads\"),\n        }\n    }\n\n    fn has_materialization_journal(root: &Path, operation_id: &str) -> bool {\n        JournalLocation::ALL.iter().any(|location| {\n            std::fs::symlink_metadata(Self::journal_path(root, *location, operation_id)).is_ok()\n        })\n    }\n\n    fn has_cleanup_journal(root: &Path, operation_id: &str) -> bool {\n        [CleanupPhase::Pending, CleanupPhase::Ready]\n            .iter()\n            .any(|phase| {\n                std::fs::symlink_metadata(Self::cleanup_path(root, *phase, operation_id)).is_ok()\n            })\n    }\n\n    fn remove_cleanup_tombstone(root: &Path, operation_id: &str) -> anyhow::Result<()> {","sourceCodeStart":1342,"sourceCodeEnd":1378,"githubUrl":"https://github.com/libnyanpasu/clash-nyanpasu/blob/f7dbce2997c633e484f54788035e770b3ee99773/backend/tauri/src/service/profile_file.rs#L1342-L1378","documentation":"When locate_cleanup finds journals in both the Pending and Ready phase slots for the same operation_id, it requires their payloads to be identical (a crash between the Windows write-through and source deletion can legitimately leave both). If the two journals disagree, the library bails rather than choosing a phase, because advancing cleanup with the wrong journal could delete the wrong profile files.","triggerScenarios":"Recovery after a crash where both cleanup phases exist but were written by different attempts sharing an operation_id, or one journal was corrupted/torn so its payload no longer matches its sibling; mixing journals restored from backups of different runs.","commonSituations":"Storage faults or forced power-off mid transition producing divergent copies; an operation_id reused across cleanup attempts; manual copying of a single phase journal during debugging, leaving a stale sibling behind.","solutions":["Discard the whole cleanup operation: remove both Pending and Ready journals for that operation_id and restart the cleanup with a new operation id.","Only if you can prove which payload is current (timestamps, transaction log), keep the matching pair by deleting the stale one and retry.","Never reuse operation ids across cleanup attempts.","Investigate disk health / crash frequency if divergent journals appear regularly."],"exampleFix":"// before: recovery cannot disambiguate\nlet state = locate_cleanup(root, &id)?;\n// after: abandon the ambiguous cleanup entirely\nstd::fs::remove_file(cleanup_path(root, CleanupPhase::Pending, &id))?;\nstd::fs::remove_file(cleanup_path(root, CleanupPhase::Ready, &id))?;\nstart_cleanup(new_plan)?; // fresh operation_id","handlingStrategy":"try-catch","validationCode":"fn cleanup_pair_agrees(root: &Path, id: &str) -> bool {\n    match (std::fs::read(cleanup_path(root, CleanupPhase::Pending, id)),\n           std::fs::read(cleanup_path(root, CleanupPhase::Ready, id))) {\n        (Ok(a), Ok(b)) => a == b,\n        _ => true,\n    }\n}","typeGuard":null,"tryCatchPattern":"match locate_cleanup(root, id) {\n    Err(e) if e.to_string().contains(\"conflicting payloads\") => {\n        // ambiguous cleanup: delete both journals and start a new cleanup attempt\n    }\n    other => other,\n}","preventionTips":["Use a unique operation_id per cleanup attempt","Never restore only one phase journal from a backup","Treat payload conflicts as corruption and abandon the operation","Monitor for crashes/storage faults that precede divergent journals"],"tags":["filesystem","crash-recovery","cleanup","data-integrity"],"backgroundTag":"internal-invariant-violation","analyzedSha":"f7dbce2997c633e484f54788035e770b3ee99773","analyzedAt":"2026-09-08T01:24:59.197Z","contentChangedAt":"2026-09-08T01:24:59.197Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}