{"record":{"id":"a43cfbcbeff83fe4","repo":"libnyanpasu/clash-nyanpasu","slug":"private-materialization-artifact-is-not-a-regular","errorCode":null,"errorMessage":"private materialization artifact is not a regular file: {}","messagePattern":"private materialization artifact is not a regular file: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/tauri/src/service/profile_file.rs","lineNumber":661,"sourceCode":"                    path.display()\n                )\n            }\n            Ok(_) => {\n                std::fs::remove_file(path)\n                    .with_context(|| format!(\"remove profile resource {}\", path.display()))?;\n                sync_directory(path.parent().expect(\"profile resource has parent\"))\n            }\n            Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(()),\n            Err(error) => {\n                Err(error).with_context(|| format!(\"inspect profile resource {}\", path.display()))\n            }\n        }\n    }\n\n    fn remove_private_regular(path: &Path) -> anyhow::Result<()> {\n        match std::fs::symlink_metadata(path) {\n            Ok(metadata) if is_symlink_or_reparse(&metadata) || !metadata.is_file() => {\n                bail!(\n                    \"private materialization artifact is not a regular file: {}\",\n                    path.display()\n                )\n            }\n            Ok(_) => {\n                std::fs::remove_file(path)\n                    .with_context(|| format!(\"remove private artifact {}\", path.display()))?;\n                sync_directory(path.parent().expect(\"private artifact has parent\"))\n            }\n            Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(()),\n            Err(error) => {\n                Err(error).with_context(|| format!(\"inspect private artifact {}\", path.display()))\n            }\n        }\n    }\n\n    fn resource_hash(resource: &MaterializationResource) -> String {\n        match resource {","sourceCodeStart":643,"sourceCodeEnd":679,"githubUrl":"https://github.com/libnyanpasu/clash-nyanpasu/blob/f7dbce2997c633e484f54788035e770b3ee99773/backend/tauri/src/service/profile_file.rs#L643-L679","documentation":"`remove_private_regular` deletes artifacts inside the library's private materialization storage (staged files, backups, journals' companions). It first verifies via `symlink_metadata` that the entry is a plain regular file; symlinks, reparse points, directories, or other node types are refused with this error to keep private storage from being manipulated through links (anti-tampering/anti-symlink-attack guard).","triggerScenarios":"Cleanup/rollback code calls `remove_private_regular` on a path under the materialization root where `symlink_metadata` reports a symlink, Windows reparse point, directory, or non-file. E.g. a user symlinked the whole staging dir elsewhere, or a directory exists where a staged artifact should be.","commonSituations":"Users symlink private storage directories to another disk to save space; cloud-sync tools replacing files with links; corrupted state where a directory was created at an artifact path.","solutions":["Stop symlink Mount of the materialization root; keep it as real directories with regular files","Delete the offending symlink/directory at the reported path and re-run the operation (artifacts are transient and safe to regenerate)","If you must relocate storage, use the application's supported data-directory setting rather than symlinks","Check for sync tools (Dropbox/OneDrive) converting these files and exclude the directory"],"exampleFix":"// before: staged artifact replaced by a symlink\nstaging/files/abc123 -> /mnt/bigdisk/abc123\n// after\nrm staging/files/abc123  # let the app recreate a regular file","handlingStrategy":"validation","validationCode":"fn private_artifact_is_regular(path: &std::path::Path) -> bool {\n    match std::fs::symlink_metadata(path) {\n        Ok(md) => md.is_file() && !md.file_type().is_symlink(),\n        Err(e) => e.kind() == std::io::ErrorKind::NotFound,\n    }\n}","typeGuard":"fn is_plain_file(md: &std::fs::Metadata) -> bool {\n    md.is_file() && !md.file_type().is_symlink()\n}","tryCatchPattern":"match cleanup_private_artifact(&path) {\n    Err(e) if e.to_string().contains(\"not a regular file\") => {\n        // unlink the symlink/dir entry so the app can recreate a real file\n        let _ = std::fs::remove_file(&path);\n    }\n    Err(e) => return Err(e),\n    Ok(()) => (),\n}","preventionTips":["Do not symlink the app's private data directories to other disks","Exclude materialization storage from cloud-sync/placeholder filesystems","Keep private storage on local, app-writable filesystems","Use the app's supported data-dir setting instead of link tricks"],"tags":["filesystem","security","symlink"],"backgroundTag":"invalid-argument-value","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"}