libnyanpasu/clash-nyanpasu · error

private materialization artifact is not a regular file: {}

Error message

private materialization artifact is not a regular file: {}

What it means

`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).

Source

Thrown at backend/tauri/src/service/profile_file.rs:661

                    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) {
            Ok(metadata) if is_symlink_or_reparse(&metadata) || !metadata.is_file() => {
                bail!(
                    "private materialization artifact is not a regular file: {}",
                    path.display()
                )
            }
            Ok(_) => {
                std::fs::remove_file(path)
                    .with_context(|| format!("remove private artifact {}", path.display()))?;
                sync_directory(path.parent().expect("private artifact has parent"))
            }
            Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(()),
            Err(error) => {
                Err(error).with_context(|| format!("inspect private artifact {}", path.display()))
            }
        }
    }

    fn resource_hash(resource: &MaterializationResource) -> String {
        match resource {

View on GitHub (pinned to f7dbce2997)

Solutions

  1. Stop symlink Mount of the materialization root; keep it as real directories with regular files
  2. Delete the offending symlink/directory at the reported path and re-run the operation (artifacts are transient and safe to regenerate)
  3. If you must relocate storage, use the application's supported data-directory setting rather than symlinks
  4. Check for sync tools (Dropbox/OneDrive) converting these files and exclude the directory

Example fix

// before: staged artifact replaced by a symlink
staging/files/abc123 -> /mnt/bigdisk/abc123
// after
rm staging/files/abc123  # let the app recreate a regular file
Defensive patterns

Strategy: validation

Validate before calling

fn private_artifact_is_regular(path: &std::path::Path) -> bool {
    match std::fs::symlink_metadata(path) {
        Ok(md) => md.is_file() && !md.file_type().is_symlink(),
        Err(e) => e.kind() == std::io::ErrorKind::NotFound,
    }
}

Type guard

fn is_plain_file(md: &std::fs::Metadata) -> bool {
    md.is_file() && !md.file_type().is_symlink()
}

Try / catch

match cleanup_private_artifact(&path) {
    Err(e) if e.to_string().contains("not a regular file") => {
        // unlink the symlink/dir entry so the app can recreate a real file
        let _ = std::fs::remove_file(&path);
    }
    Err(e) => return Err(e),
    Ok(()) => (),
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08). Data as JSON: /api/errors/a43cfbcbeff83fe4. Report an issue: GitHub.