{"record":{"id":"53acfdedf9e3f860","repo":"libnyanpasu/clash-nyanpasu","slug":"materialization-journal-operation-id-mismatch","errorCode":null,"errorMessage":"materialization journal operation id mismatch","messagePattern":"materialization journal operation id mismatch","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/tauri/src/service/profile_file.rs","lineNumber":622,"sourceCode":"        set_private_file_permissions(path)?;\n        sync_directory(path.parent().expect(\"journal has parent\"))\n    }\n\n    fn read_journal(path: &Path, operation_id: &str) -> anyhow::Result<MaterializationJournal> {\n        let metadata = std::fs::symlink_metadata(path)\n            .with_context(|| format!(\"inspect materialization journal {}\", path.display()))?;\n        if is_symlink_or_reparse(&metadata) || !metadata.is_file() {\n            bail!(\n                \"materialization journal is not a regular file: {}\",\n                path.display()\n            );\n        }\n        let content = std::fs::read_to_string(path)\n            .with_context(|| format!(\"read materialization journal {}\", path.display()))?;\n        let journal: MaterializationJournal = serde_yaml::from_str(&content)\n            .with_context(|| format!(\"parse materialization journal {}\", path.display()))?;\n        if journal.operation_id != operation_id || !valid_operation_id(&journal.operation_id) {\n            bail!(\"materialization journal operation id mismatch\");\n        }\n        if journal\n            .managed_path\n            .as_path()\n            .components()\n            .any(|component| is_materialization_root_name(component.as_os_str()))\n        {\n            bail!(\"materialization journal targets reserved private storage\");\n        }\n        if journal.hash.len() != 64 || !journal.hash.bytes().all(|byte| byte.is_ascii_hexdigit()) {\n            bail!(\"materialization journal hash is invalid\");\n        }\n        Ok(journal)\n    }\n\n    fn remove_nofollow(path: &Path) -> anyhow::Result<()> {\n        match std::fs::symlink_metadata(path) {\n            Ok(metadata) if metadata.is_dir() && !is_symlink_or_reparse(&metadata) => {","sourceCodeStart":604,"sourceCodeEnd":640,"githubUrl":"https://github.com/libnyanpasu/clash-nyanpasu/blob/f7dbce2997c633e484f54788035e770b3ee99773/backend/tauri/src/service/profile_file.rs#L604-L640","documentation":"`read_journal` validates that the parsed `MaterializationJournal`'s `operation_id` equals the operation id used to compute the journal path, and that the id passes `valid_operation_id` (a well-formed identifier). A mismatch means the journal file at `<operation_id>.yaml` contains a different or malformed operation id, so it cannot be trusted as the record for this operation. The library refuses to use it to drive recovery.","triggerScenarios":"Calling recovery/commit code paths that call `read_journal(path, operation_id)` where the YAML on disk stores a different `operation_id`, an empty id, or one failing `valid_operation_id` (e.g. containing path separators, `..`, or invalid characters). Typically caused by copying/renaming journal files by hand or leftover journals from a different operation.","commonSituations":"Manually copying journal YAML files between operation directories; restoring a partial backup of the materialization root where file names and contents no longer correspond; hand-edited journals; stale journals from an aborted upgrade.","solutions":["Delete the mismatched journal file `<root>/<location>/<operation_id>.yaml` so the operation is treated as unknown and re-run materialization","Do not rename or copy journal files; each journal is keyed by its operation id","If migrating data, use the app's own export/import rather than copying the materialization directory","Verify the YAML's `operation_id` field matches the file name if you must inspect it manually"],"exampleFix":"// before: file staging/journals/def456.yaml containing\n// operation_id: abc123\nmv staging/journals/def456.yaml /tmp/inspect/\n// after: let the app recreate a journal for the current operation id\n# remove mismatched journal, re-run profile materialization","handlingStrategy":"validation","validationCode":"fn valid_op_id(id: &str) -> bool {\n    !id.is_empty() && id.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'-' || b == b'_')\n}\n// before trusting a journal file, check name <-> content correspondence:\n// file_name stem must equal journal.operation_id and valid_op_id must hold","typeGuard":"fn journal_matches(j: &MaterializationJournal, op_id: &str) -> bool {\n    j.operation_id == op_id && valid_op_id(&j.operation_id)\n}","tryCatchPattern":"match read_journal(&path, &op_id) {\n    Err(e) if e.to_string().contains(\"operation id mismatch\") => {\n        let _ = std::fs::remove_file(&path); // stale/tampered journal; re-run operation\n    }\n    Err(e) => return Err(e),\n    Ok(j) => use_journal(j),\n}","preventionTips":["Never rename or copy journal files between operation ids","Keep journal file names identical to their operation_id field","Restore the materialization root only via app-supported backup flows","Validate operation ids (alphanumeric/-/_) before writing journals"],"tags":["filesystem","integrity","validation"],"backgroundTag":"invalid-identifier-format","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"}