libnyanpasu/clash-nyanpasu · error
existing non-symlink file at {}, refusing to replace
Error message
existing non-symlink file at {}, refusing to replace What it means
ProfileFileService::ensure_symlink refuses to replace an existing regular file (or directory) at a managed path with a symlink. This is a deliberate safety fence against destroying user data: the service will only swap out an existing symlink, never delete a real file that it did not create. The caller must explicitly remove the non-symlink entry before requesting a symlink there.
Source
Thrown at backend/tauri/src/service/profile_file.rs:1580
Err(e) => Err(e).with_context(|| format!("inspect profile file {}", full.display())),
}
}
fn ensure_symlink(
&self,
path: &ManagedProfilePath,
target: &ExternalProfilePath,
) -> anyhow::Result<()> {
let full = self.resolve(path)?;
self.ensure_managed_parent(&full)?;
match std::fs::symlink_metadata(&full) {
Ok(meta) if meta.file_type().is_symlink() => {
if symlink_points_to(&full, target.as_path())? {
return Ok(());
}
std::fs::remove_file(&full)?;
}
Ok(_) => bail!(
"existing non-symlink file at {}, refusing to replace",
full.display()
),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {}
Err(e) => Err(e).with_context(|| format!("inspect profile file {}", full.display()))?,
}
#[cfg(windows)]
std::os::windows::fs::symlink_file(target.as_path(), &full)?;
#[cfg(unix)]
std::os::unix::fs::symlink(target.as_path(), &full)?;
Ok(())
}
}
impl ProfileMaterializationPort for ProfileFileService {
fn prepare_state_first(
&self,
path: &ManagedProfilePath,View on GitHub (pinned to f7dbce2997)
Solutions
- Remove the existing regular file at the reported path (after backing up its contents) and retry ensure_symlink.
- If the file's contents should be preserved, move them to the intended external target path first, then remove the file and call ensure_symlink.
- Check for sync/backup software that materializes symlinks into real files and exclude the app data directory.
- Use the service's remove(path) API instead of writing through the path, so the managed entry is deleted before linking.
Example fix
// before
// profile file was materialized as a regular file; this now fails
service.ensure_symlink(&path, &external)?;
// after
let full = service.resolve(&path)?;
if std::fs::symlink_metadata(&full).map(|m| !m.file_type().is_symlink()).unwrap_or(false) {
std::fs::rename(&full, backup_path)?; // preserve contents
}
service.ensure_symlink(&path, &external)?; Defensive patterns
Strategy: validation
Validate before calling
let full = service.resolve(&path)?;
match std::fs::symlink_metadata(&full) {
Ok(m) if m.file_type().is_symlink() => Ok(()),
Ok(_) => Err(anyhow!("{} is a regular file; move it to the external target first", full.display())),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(()),
Err(e) => Err(e.into()),
} Type guard
fn is_symlink(p: &std::path::Path) -> bool {
std::fs::symlink_metadata(p).map(|m| m.file_type().is_symlink()).unwrap_or(false)
} Prevention
- Back up and remove any regular file at a managed path before switching that path to a symlink layout.
- Exclude app data directories from file-sync tools that materialize symlinks.
- After restoring from backups, verify managed paths are symlinks, not copied regular files.
- Always use the service's remove/ensure_symlink APIs rather than raw filesystem writes in the managed tree.
When it happens
Trigger: Calling ProfileFileService::ensure_symlink(path, target) when `path` already exists as a regular file (symlink_metadata succeeds and the entry is not a symlink). Occurs when a profile item migrates from an inline/file-backed layout to an external-link layout while a real file still occupies the managed path, or a stale non-symlink file was left behind by a previous version or manual copy.
Common situations: Upgrading from an older app version that stored the profile as a regular file while the new version links to an external target; the user (or a sync tool like Dropbox/OneDrive) dropped a real file where the app expects to place its symlink; a restore/backup tool replaced symlinks with real file copies.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
- runtime candidate directory is a symlink or reparse point: {
- profile directory is a symlink, reparse point, or non-direct
- materialization journal is not a regular file: {}
- private materialization artifact is not a regular file: {}
- staged file is not a regular file
AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08).
Data as JSON: /api/errors/900cc1e2f19a1a0a.
Report an issue: GitHub.