{"record":{"id":"35343ccbe1dc04e8","repo":"libnyanpasu/clash-nyanpasu","slug":"pending-cleanup-journal-is-not-a-regular-file","errorCode":null,"errorMessage":"pending cleanup journal is not a regular file","messagePattern":"pending cleanup journal is not a regular file","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/tauri/src/service/profile_file.rs","lineNumber":1340,"sourceCode":"                    .map(|source| source.materialized().file.clone())\n            })\n            .collect()\n    }\n\n    fn locate_cleanup(\n        root: &Path,\n        operation_id: &str,\n    ) -> anyhow::Result<Option<(CleanupPhase, MaterializationJournal)>> {\n        if !valid_operation_id(operation_id) {\n            bail!(\"invalid profile cleanup operation id\");\n        }\n        let pending_path = Self::cleanup_path(root, CleanupPhase::Pending, operation_id);\n        let ready_path = Self::cleanup_path(root, CleanupPhase::Ready, operation_id);\n        let pending = match std::fs::symlink_metadata(&pending_path) {\n            Ok(metadata) if !is_symlink_or_reparse(&metadata) && metadata.is_file() => {\n                Some(Self::read_journal(&pending_path, operation_id)?)\n            }\n            Ok(_) => bail!(\"pending 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 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)))","sourceCodeStart":1322,"sourceCodeEnd":1358,"githubUrl":"https://github.com/libnyanpasu/clash-nyanpasu/blob/f7dbce2997c633e484f54788035e770b3ee99773/backend/tauri/src/service/profile_file.rs#L1322-L1358","documentation":"While locating a cleanup journal, locate_cleanup inspects the Pending phase path with symlink_metadata. If the path exists but is anything other than a plain regular file (symlink, reparse point, directory), it bails: recovery code must not read through or delete foreign filesystem objects. A journal that exists but is not a regular file means external interference in the private journal directory.","triggerScenarios":"Running cleanup lookup/recovery for an operation whose Pending cleanup journal path exists as a symlink, Windows reparse point, or directory — caused by cloud-sync placeholders, quarantine stubs, or manual tampering with the journal directory.","commonSituations":"OneDrive/Dropbox converting stale journal files to on-demand placeholders on Windows; antivirus replacing the file after quarantine; a developer creating a directory at that path while debugging; symlink attacks if the profiles root is on a shared/writable location.","solutions":["Inspect the reported Pending journal path, remove the non-file artifact, and rerun recovery (a fresh attempt can then be started or the previous one abandoned).","Exclude the private journal directory from cloud sync, backup, and antivirus scanning.","If the profiles root may be writable by other users, move it to a user-private location to rule out symlink planting.","Abandon the operation by clearing all journals/tombstones for that operation_id and re-plan the cleanup."],"exampleFix":"// before: recovery keeps failing on the bogus artifact\nlet state = locate_cleanup(root, &id)?;\n// after: clear the non-regular Pending artifact first\nlet p = cleanup_path(root, CleanupPhase::Pending, &id);\nlet meta = std::fs::symlink_metadata(&p)?;\nif meta.is_symlink() || !meta.is_file() {\n    std::fs::remove_file(&p)?; // or remove_dir_all if a directory\n}\nlet state = locate_cleanup(root, &id)?;","handlingStrategy":"validation","validationCode":"fn pending_journal_clean(root: &Path, id: &str) -> Result<bool, std::io::Error> {\n    match std::fs::symlink_metadata(cleanup_path(root, CleanupPhase::Pending, id)) {\n        Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(true),\n        Err(e) => Err(e),\n        Ok(m) => Ok(!m.is_symlink() && m.is_file()),\n    }\n}","typeGuard":"fn is_plain_file(m: &std::fs::Metadata) -> bool {\n    !m.is_symlink() && m.is_file()\n}","tryCatchPattern":"match locate_cleanup(root, id) {\n    Err(e) if e.to_string().contains(\"pending cleanup journal is not a regular file\") => {\n        // remove the artifact, then either retry recovery or abandon the operation\n    }\n    other => other,\n}","preventionTips":["Exclude journal directories from cloud-sync placeholder features","Keep the profiles root user-private to prevent symlink planting","Inspect and remove foreign artifacts before running recovery","If interference recurs, audit which external tool touches the directory"],"tags":["filesystem","symlink","cleanup","recovery"],"backgroundTag":"incompatible-source-type","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"}