jdx/mise · error

brew-cask: generic artifact backup changed directories

Error message

brew-cask: generic artifact backup changed directories

What it means

The backup/restore rename of a generic artifact is performed with renameat on a single opened parent directory descriptor, which only works when source and destination share one directory. rename_trusted_generic_target therefore requires from.parent() == to.parent() and refuses when the backup path lives in a different directory.

Source

Thrown at src/system/packages/brew/cask.rs:2488

fn validate_trusted_operation_parent(
    parent: &TrustedOperationParent,
    expected_parent: &Path,
) -> Result<()> {
    let actual_parent = std::fs::canonicalize(parent.path()?)?;
    if actual_parent != expected_parent {
        bail!(
            "brew-cask: refusing operation through a changed generic artifact parent: {}",
            expected_parent.display()
        );
    }
    Ok(())
}

fn rename_trusted_generic_target(from: &Path, to: &Path, expected_parent: &Path) -> Result<()> {
    #[cfg(unix)]
    {
        if from.parent() != to.parent() {
            bail!("brew-cask: generic artifact backup changed directories");
        }
        let parent = open_trusted_operation_parent(from, true, false)?;
        validate_trusted_operation_parent(&parent, expected_parent)?;
        let from_name = from
            .file_name()
            .ok_or_else(|| eyre!("brew-cask: generic artifact source has no filename"))?;
        let to_name = to
            .file_name()
            .ok_or_else(|| eyre!("brew-cask: generic artifact target has no filename"))?;
        nix::fcntl::renameat(&parent.fd, from_name, &parent.fd, to_name)?;
        Ok(())
    }
    #[cfg(not(unix))]
    {
        let _ = expected_parent;
        file::rename(from, to)
    }
}

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Build the backup path in the same directory as the target: target.with_file_name(format!(".{}.mise-old", name))
  2. If calling rename_trusted_generic_target directly, assert from.parent() == to.parent() beforehand
  3. Keep backup/restore inside one directory so the single-parent rename stays valid

Example fix

// before -- backup created in another directory
let backup = std::env::temp_dir().join(target.file_name().unwrap());

// after -- same parent as the target
let backup = target.with_file_name(format!(".{}.mise-old", target.file_name().unwrap().to_string_lossy()));
Defensive patterns

Strategy: validation

Validate before calling

if from.parent() != to.parent() {
    anyhow::bail!("backup path must share the target's directory: {} vs {}",
        from.display(), to.display());
}

Prevention

When it happens

Trigger: A caller constructing the backup path under a different directory (e.g. a temp dir) than the live target; any code path where the '.mise-old' backup name is appended to a different base path.

Common situations: Custom wrappers moving backups to /tmp; orchestration bugs mixing up source and target parents; almost never caused by end-user configuration.

Related errors


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/bc6ff41f56672156. Report an issue: GitHub.