jdx/mise · error

brew-cask: refusing operation through a changed generic arti

Error message

brew-cask: refusing operation through a changed generic artifact parent: {}

What it means

Trusted generic-artifact operations open the parent directory by file descriptor and later re-verify (canonicalize) that the opened directory still matches the expected parent path. If the directory was swapped, renamed, or replaced by a symlink between open and use, the operation is aborted — a TOCTOU defense against an attacker replacing the directory mid-operation.

Source

Thrown at src/system/packages/brew/cask/mod.rs:2320

            .file_name()
            .ok_or_else(|| eyre!("brew-cask: generic artifact target has no filename"))?;
        remove_all_at(&parent.fd, name)
    }
    #[cfg(not(unix))]
    {
        let _ = expected_parent;
        file::remove_all(target)
    }
}

#[cfg(unix)]
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"))?;

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Re-run the install/upgrade command — the race usually does not recur
  2. Exclude the mise prefix directory from file-sync/AV real-time scanning
  3. Avoid running concurrent mise operations on the same toolset

Example fix

// before
mise install <cask> &  mise upgrade <cask> &   # concurrent
// after
mise install <cask> && mise upgrade <cask>     # sequential
Defensive patterns

Strategy: retry

Try / catch

match result {
    Err(e) if e.to_string().contains("changed generic artifact parent") => {
        // transient race: retry once after excluding concurrent writers
        eprintln!("directory changed mid-operation; retry without concurrency");
    }
    Err(e) => return Err(e),
    Ok(v) => v,
}

Prevention

When it happens

Trigger: During backup/restore (rename) or removal of a generic artifact, the directory that was opened via open_trusted_operation_parent no longer canonicalizes to expected_parent (e.g. it was deleted and recreated, or moved by another process).

Common situations: Another mise/Homebrew process concurrently modifying the same cask directory; antivirus or sync tools (Dropbox/iCloud) moving directories during install; a race during parallel upgrades.

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 jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/50cef086880caff2. Report an issue: GitHub.