{"record":{"id":"7633ee0b4d5038ea","repo":"libnyanpasu/clash-nyanpasu","slug":"refusing-to-remove-directory-as-a-profile-resource","errorCode":null,"errorMessage":"refusing to remove directory as a profile resource: {}","messagePattern":"refusing to remove directory as a profile resource: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/tauri/src/service/profile_file.rs","lineNumber":641,"sourceCode":"        }\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) => {\n                bail!(\n                    \"refusing to remove directory as a profile resource: {}\",\n                    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) {","sourceCodeStart":623,"sourceCodeEnd":659,"githubUrl":"https://github.com/libnyanpasu/clash-nyanpasu/blob/f7dbce2997c633e484f54788035e770b3ee99773/backend/tauri/src/service/profile_file.rs#L623-L659","documentation":"`remove_nofollow` deletes a managed profile resource using no-follow semantics: it inspects with `symlink_metadata` and calls `remove_file`. If the path is a real directory (not a symlink/reparse point), `remove_file` would fail confusingly (or on some platforms behave unexpectedly), so the code refuses up front with this error. It protects against recursively significant structures being destroyed as if they were single files.","triggerScenarios":"Removing a profile resource whose path (e.g. a managed profile file or backup link path) is an actual directory on disk. Happens when a user created a directory where the app expects a file/symlink, or a previous materialization left a directory in place of the managed item.","commonSituations":"User creates a folder with the same name as an expected profile file (e.g. `config.yaml/`); dotfile managers replacing files with directories; an interrupted operation left a directory at a managed path.","solutions":["Inspect the reported path; if the directory contents are not needed, remove it manually (`rm -r` / `Remove-Item -Recurse`) so the app can recreate it as a file/symlink","If the directory holds user data, move it elsewhere first, then delete the empty directory","Re-run the profile materialization after clearing the path","Do not place directories at paths the app manages as files"],"exampleFix":"// before: managed path is a directory\n~/.config/nyanpasu/profiles/config.yaml/   (directory)\n// after\nmv ~/.config/nyanpasu/profiles/config.yaml ~/config.yaml.backup\nrmdir ~/.config/nyanpasu/profiles/config.yaml","handlingStrategy":"validation","validationCode":"fn removable_as_file(path: &std::path::Path) -> bool {\n    match std::fs::symlink_metadata(path) {\n        Ok(md) => !(md.is_dir() && !md.file_type().is_symlink()),\n        Err(e) => e.kind() == std::io::ErrorKind::NotFound,\n    }\n}\n// check before invoking operations that remove managed resources","typeGuard":"fn is_real_dir(md: &std::fs::Metadata) -> bool {\n    md.is_dir() && !md.file_type().is_symlink()\n}","tryCatchPattern":"if let Err(e) = remove_managed(&path) {\n    if e.to_string().contains(\"refusing to remove directory\") {\n        // preserve contents, then clear the directory manually before retrying\n        std::fs::read_dir(&path)?.for_each(...); // backup, then remove_dir_all\n    } else { return Err(e); }\n}","preventionTips":["Never create directories at paths the app manages as files","Check for name collisions before unpacking configs into managed locations","After restoring backups, verify managed paths are files/symlinks, not dirs","Let the app own the creation/removal of managed paths"],"tags":["filesystem","path-is-a-directory"],"backgroundTag":"path-is-not-a-directory","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"}