jdx/mise · error
brew-cask: generic artifact backup changed directories
Error message
brew-cask: generic artifact backup changed directories
What it means
rename_trusted_generic_target atomically renames a generic artifact target to its backup name using renameat against a single trusted parent directory fd. It first requires from.parent() == to.parent() — the backup must live in the same directory as the target so the rename never crosses directories. Because the backup is normally derived via target.with_extension("mise-old-<hash>") (which preserves the parent), a mismatch is an internal-invariant breach: the two paths were built from different bases.
Solutions
- If calling this from code, derive the backup with target.with_extension(...) so it shares the target's parent
- Re-run once to rule out a transient filesystem quirk
- If it persists, capture it with MISE_DEBUG=1 (or RUST_BACKTRACE=1) and file an issue — the invariant should hold for every validated target
Example fix
// before (caller code)
let backup = backups_dir.join(target.file_name().unwrap());
rename_trusted_generic_target(&target, &backup, &parent)?;
// after
let backup = target.with_extension(format!("mise-old-{}", hash));
rename_trusted_generic_target(&target, &backup, &parent)?; Defensive patterns
Strategy: validation
Validate before calling
// If you call rename_trusted_generic_target yourself, keep both paths in one dir.
let backup = target.with_extension(format!("mise-old-{}", short_hash(&target)));
assert_eq!(target.parent(), backup.parent()); Prevention
- Derive backup paths from the target via with_extension so the parent is shared
- Never pass hand-built absolute backup paths into the trusted rename API
When it happens
Trigger: Programmatic callers passing a backup path located in a different directory; path normalization that changes the computed parent (trailing separators, surviving '..' components); not a configuration-level error for end users.
Common situations: Forks or custom tooling that construct backup paths themselves; essentially unseen in normal CLI-driven installs.
Related errors
- brew-cask: generic artifact backup changed directories
- brew-cask: generic artifact source has no filename
- brew-cask: generic artifact target
- brew-cask: internal non-terminate flight step
- brew-cask: invalid generic artifact parent
AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22).
Data as JSON: /api/errors/bc6ff41f56672156.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/packages/brew/cask.rs:2614
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 6f52dcdf99)