{"record":{"id":"01ff6b3e2da1d842","repo":"libnyanpasu/clash-nyanpasu","slug":"materialization-operation-has-conflicting-journal","errorCode":null,"errorMessage":"materialization operation has conflicting journal payloads in {duplicate_location:?}","messagePattern":"materialization operation has conflicting journal payloads in (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/tauri/src/service/profile_file.rs","lineNumber":1162,"sourceCode":"    fn converge_materialization_journals(\n        mut found: Vec<(JournalLocation, MaterializationJournal, PathBuf)>,\n    ) -> anyhow::Result<Option<(JournalLocation, MaterializationJournal)>> {\n        let Some(family) = found.first().map(|(location, _, _)| location.family()) else {\n            return Ok(None);\n        };\n        if found\n            .iter()\n            .any(|(location, _, _)| location.family() != family)\n        {\n            bail!(\"materialization operation has mixed transaction families\");\n        }\n        found.sort_by_key(|(location, _, _)| location.rank());\n        let (location, journal, _) = found\n            .pop()\n            .expect(\"non-empty materialization journal set has a preferred phase\");\n        for (duplicate_location, duplicate_journal, duplicate_path) in found {\n            if duplicate_journal != journal {\n                bail!(\n                    \"materialization operation has conflicting journal payloads in {duplicate_location:?}\"\n                );\n            }\n            Self::remove_private_regular(&duplicate_path)?;\n        }\n        Ok(Some((location, journal)))\n    }\n\n    /// The journal locations share one private root, so Unix `rename` is an\n    /// atomic same-filesystem phase transition. Do not use `move_atomic`: its\n    /// hard-link/unlink fallback can leave duplicate phase artifacts.\n    #[allow(dead_code)]\n    fn rename_journal_same_filesystem(source: &Path, destination: &Path) -> anyhow::Result<()> {\n        let metadata = std::fs::symlink_metadata(source)\n            .with_context(|| format!(\"inspect journal source {}\", source.display()))?;\n        if is_symlink_or_reparse(&metadata) || !metadata.is_file() {\n            bail!(\"journal source is not a regular file: {}\", source.display());\n        }","sourceCodeStart":1144,"sourceCodeEnd":1180,"githubUrl":"https://github.com/libnyanpasu/clash-nyanpasu/blob/f7dbce2997c633e484f54788035e770b3ee99773/backend/tauri/src/service/profile_file.rs#L1144-L1180","documentation":"After selecting the highest-rank journal, converge_materialization_journals compares each duplicate (lower-rank) journal payload against the preferred one; a mismatch means the same operation has divergent journal contents across locations. Since the code cannot know which payload reflects the true transaction state, it aborts instead of guessing and possibly promoting the wrong data.","triggerScenarios":"Duplicate journals for one operation id contain different payloads — typically when a same-filesystem rename fell back to hard-link/unlink and a crash left a stale copy of an earlier phase's journal, or journals were partially updated before a crash.","commonSituations":"Crash/power loss between writing the new-phase journal and removing the old-phase duplicate; filesystems where rename is not atomic; manual copying of journal files between locations.","solutions":["Manually inspect both journal payloads and keep the one matching the actual on-disk state (staged resource, ready link); delete the stale duplicate and rerun reconcile/complete.","If state cannot be determined, remove all journals for the operation id and compensate/re-materialize from scratch.","Ensure staging lives on a filesystem with atomic rename to avoid the hard-link/unlink fallback path.","Check for crashes correlated with journal phase transitions; UPS/graceful shutdown reduces recurrence."],"exampleFix":"// before\n// crash left old-phase journal with stale payload alongside new-phase journal\n// after\nstd::fs::remove_file(&stale_duplicate_path)?; // keep highest-rank journal\nclient.complete(root, operation_id).await?;","handlingStrategy":"try-catch","validationCode":"// compare duplicate journal payloads before resuming\nlet payloads = read_all_journal_payloads(root, op_id)?;\nif payloads.iter().any(|p| p != &payloads[0]) {\n    // decide from on-disk state which payload is authoritative, delete stale duplicates\n}","typeGuard":null,"tryCatchPattern":"match client.complete(root, op_id).await {\n    Err(e) if e.to_string().contains(\"conflicting journal payloads\") => {\n        // cannot auto-resolve: inspect manually or compensate from scratch\n        client.compensate(root, op_id).await?;\n        let fresh = client.allocate_operation_id(root).await?;\n        /* re-materialize */\n        bail!(\"operation rolled back due to conflicting journals\")\n    }\n    r => r,\n}","preventionTips":["Keep staging on a filesystem with atomic rename to avoid hard-link/unlink duplicates.","Protect the machine from power loss during profile operations (journaling filesystems, UPS).","After crashes, always run reconcile before resuming operations.","Don't manually copy journal files between phase locations."],"tags":["journal","consistency","crash"],"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"}