libnyanpasu/clash-nyanpasu · error

materialization has multiple backups

Error message

materialization has multiple backups

What it means

read_backup_resource reads the rollback backup for an operation and expects at most one backup resource (a file blob OR a symlink-spec file) per operation_id under the backup root. Both the backup file path and the backup link path exist, which is an inconsistent state, so the code aborts rather than restoring an ambiguous backup.

Source

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

        let link_path = Self::backup_link_path(root, operation_id);
        let file_metadata = match std::fs::symlink_metadata(&file_path) {
            Ok(metadata) => Some(metadata),
            Err(error) if error.kind() == std::io::ErrorKind::NotFound => None,
            Err(error) => {
                return Err(error)
                    .with_context(|| format!("inspect backup file {}", file_path.display()));
            }
        };
        let link_metadata = match std::fs::symlink_metadata(&link_path) {
            Ok(metadata) => Some(metadata),
            Err(error) if error.kind() == std::io::ErrorKind::NotFound => None,
            Err(error) => {
                return Err(error)
                    .with_context(|| format!("inspect backup link {}", link_path.display()));
            }
        };
        if file_metadata.is_some() && link_metadata.is_some() {
            bail!("materialization has multiple backups");
        }
        if let Some(metadata) = file_metadata {
            if is_symlink_or_reparse(&metadata) || !metadata.is_file() {
                bail!("backup file is not a regular file");
            }
            return Ok(Some(StoredResource::File { path: file_path }));
        }
        if let Some(metadata) = link_metadata {
            if is_symlink_or_reparse(&metadata) || !metadata.is_file() {
                bail!("backup symlink specification is not a regular file");
            }
            let target = std::fs::read_to_string(&link_path)?;
            return Ok(Some(StoredResource::Symlink {
                target: ExternalProfilePath::new(target)?,
            }));
        }
        Ok(None)
    }

View on GitHub (pinned to f7dbce2997)

Solutions

  1. Delete the backup directory contents for this operation_id and re-run the operation so exactly one backup is captured.
  2. Re-run with a fresh operation_id to get a clean backup area.
  3. Ensure operation cleanup (removal of backup files) runs after successful promotion, and serialize operations sharing an operation_id.
  4. If one of the two files is clearly stale (older mtime), remove it — but prefer a clean re-run.
Defensive patterns

Strategy: try-catch

Validate before calling

let f = backup_file_path(root, op_id);
let l = backup_link_path(root, op_id);
if f.exists() && l.exists() {
    std::fs::remove_dir_all(backup_dir(root, op_id))?; // clear ambiguous backups
}

Try / catch

match result {
    Err(e) if e.to_string().contains("multiple backups") => {
        clear_backup_dir(root, op_id)?;
        retry_with_fresh_operation_id()
    }
    other => other,
}

Prevention

When it happens

Trigger: Both backup_file_path(root, operation_id) and backup_link_path(root, operation_id) exist when read_backup_resource is called — e.g. the same operation_id was used for two materializations where the managed target was first a file and then a symlink, and the earlier backup was never cleaned up.

Common situations: Reused operation IDs across sequential materializations; a failed run that wrote one backup kind, then a retried run wrote the other without cleanup; manual restoration of backup directories merging old and new backups.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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