libnyanpasu/clash-nyanpasu · error
refusing to remove directory as a profile resource: {}
Error message
refusing to remove directory as a profile resource: {} What it means
`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.
Source
Thrown at backend/tauri/src/service/profile_file.rs:641
}
if journal
.managed_path
.as_path()
.components()
.any(|component| is_materialization_root_name(component.as_os_str()))
{
bail!("materialization journal targets reserved private storage");
}
if journal.hash.len() != 64 || !journal.hash.bytes().all(|byte| byte.is_ascii_hexdigit()) {
bail!("materialization journal hash is invalid");
}
Ok(journal)
}
fn remove_nofollow(path: &Path) -> anyhow::Result<()> {
match std::fs::symlink_metadata(path) {
Ok(metadata) if metadata.is_dir() && !is_symlink_or_reparse(&metadata) => {
bail!(
"refusing to remove directory as a profile resource: {}",
path.display()
)
}
Ok(_) => {
std::fs::remove_file(path)
.with_context(|| format!("remove profile resource {}", path.display()))?;
sync_directory(path.parent().expect("profile resource has parent"))
}
Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(()),
Err(error) => {
Err(error).with_context(|| format!("inspect profile resource {}", path.display()))
}
}
}
fn remove_private_regular(path: &Path) -> anyhow::Result<()> {
match std::fs::symlink_metadata(path) {View on GitHub (pinned to f7dbce2997)
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
Example fix
// before: managed path is a directory ~/.config/nyanpasu/profiles/config.yaml/ (directory) // after mv ~/.config/nyanpasu/profiles/config.yaml ~/config.yaml.backup rmdir ~/.config/nyanpasu/profiles/config.yaml
Defensive patterns
Strategy: validation
Validate before calling
fn removable_as_file(path: &std::path::Path) -> bool {
match std::fs::symlink_metadata(path) {
Ok(md) => !(md.is_dir() && !md.file_type().is_symlink()),
Err(e) => e.kind() == std::io::ErrorKind::NotFound,
}
}
// check before invoking operations that remove managed resources Type guard
fn is_real_dir(md: &std::fs::Metadata) -> bool {
md.is_dir() && !md.file_type().is_symlink()
} Try / catch
if let Err(e) = remove_managed(&path) {
if e.to_string().contains("refusing to remove directory") {
// preserve contents, then clear the directory manually before retrying
std::fs::read_dir(&path)?.for_each(...); // backup, then remove_dir_all
} else { return Err(e); }
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- managed target is not a file: {}
- managed target is not replaceable: {}
- failed to allocate a unique runtime candidate after 16 attem
- runtime candidate directory is a symlink or reparse point: {
- runtime candidate path is not a private directory: {path}
AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08).
Data as JSON: /api/errors/7633ee0b4d5038ea.
Report an issue: GitHub.