{"record":{"id":"51776eb696daf2e7","repo":"libnyanpasu/clash-nyanpasu","slug":"profile-directory-is-a-symlink-reparse-point-or","errorCode":null,"errorMessage":"profile directory is a symlink, reparse point, or non-directory: {}","messagePattern":"profile directory is a symlink, reparse point, or non-directory: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/tauri/src/service/profile_file.rs","lineNumber":394,"sourceCode":"\nimpl CleanupPhase {\n    fn directory(self) -> &'static str {\n        match self {\n            Self::Pending => \"cleanup/pending\",\n            Self::Ready => \"cleanup/ready\",\n        }\n    }\n}\n\n#[derive(Debug)]\nenum StoredResource {\n    File { path: PathBuf },\n    Symlink { target: ExternalProfilePath },\n}\n\nfn ensure_real_directory(path: &Path, metadata: &std::fs::Metadata) -> anyhow::Result<()> {\n    if is_symlink_or_reparse(metadata) || !metadata.is_dir() {\n        bail!(\n            \"profile directory is a symlink, reparse point, or non-directory: {}\",\n            path.display()\n        );\n    }\n    Ok(())\n}\n\nfn ensure_real_directory_tree(path: &Path) -> anyhow::Result<()> {\n    match std::fs::symlink_metadata(path) {\n        Ok(metadata) => ensure_real_directory(path, &metadata),\n        Err(error) if error.kind() == std::io::ErrorKind::NotFound => {\n            let parent = path\n                .parent()\n                .context(\"directory creation path has no parent\")?;\n            ensure_real_directory_tree(parent)?;\n            let created = match std::fs::create_dir(path) {\n                Ok(()) => true,\n                Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => false,","sourceCodeStart":376,"sourceCodeEnd":412,"githubUrl":"https://github.com/libnyanpasu/clash-nyanpasu/blob/f7dbce2997c633e484f54788035e770b3ee99773/backend/tauri/src/service/profile_file.rs#L376-L412","documentation":"ensure_real_directory validates that a directory on the profile parent chain is a genuine directory: if the filesystem metadata reports a symlink/reparse point, or the entry is not a directory, the operation is aborted. The app refuses to traverse or create through link-like entries in the profiles directory tree to keep its private storage from being redirected.","triggerScenarios":"Any code path that walks or builds the profiles directory chain (validate_existing_parent_chain, ensure_profiles_root, ensure_directory_chain, ensure_real_directory_tree) encountering an entry whose metadata has symlink/reparse attributes or is a plain file where a directory is required — e.g. 'profiles/sub' is an NTFS junction, an alias, or a file named 'sub'.","commonSituations":"Cloud-sync clients replacing profile folders with reparse points; a user creating a file where the app expects a directory (e.g. a file literally named like the profiles root or a group folder); restored backups containing symlinks; macOS/Linux dotfile managers (stow, symlinking configs) linking profile directories.","solutions":["Replace the symlink/junction with a real directory and move any content into it, then retry","Delete any plain file occupying the required directory name so the app can create the directory itself","Re-run backup/restore or sync excluding the app's profiles directory so links are not recreated","Check entries beforehand with symlink_metadata (is_symlink/file_attributes reparse bit) and fail fast with your own message"],"exampleFix":"// before (unix)\nln -s /elsewhere/profiles \"$root/profiles/sub\"\n// after (unix)\nrm \"$root/profiles/sub\" && mkdir \"$root/profiles/sub\" && cp -r /elsewhere/profiles/. \"$root/profiles/sub/\"","handlingStrategy":"validation","validationCode":"use std::path::Path;\npub fn is_real_directory(path: &Path) -> bool {\n    match std::fs::symlink_metadata(path) {\n        Ok(md) => !is_symlink_or_reparse(&md) && md.is_dir(),\n        Err(_) => false,\n    }\n}\n// unix: md.file_type().is_symlink(); windows: (md.file_attributes() & 0x400) != 0 (FILE_ATTRIBUTE_REPARSE_POINT)","typeGuard":null,"tryCatchPattern":"match client.get_profiles().await {\n    Err(e) if e.to_string().contains(\"symlink, reparse point, or non-directory\") => {\n        eprintln!(\"replace links/files in the profiles directory with real directories, then retry\");\n    }\n    other => other?,\n}","preventionTips":["Do not symlink profile folders via dotfile managers or cloud-drive reparse points","Ensure nothing (files included) occupies names the app needs as directories under profiles","Exclude the profiles directory from tools that rewrite folders as links/junctions","After restore/migration, audit the tree with symlink_metadata and delete or materialize any links"],"tags":["filesystem","symlink","security","directories"],"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"}