jdx/mise · error
brew-cask: refusing operation through a changed generic…
Error message
brew-cask: refusing operation through a changed generic artifact parent: {} What it means
validate_trusted_operation_parent re-canonicalizes the path of the directory fd opened by open_trusted_operation_parent and compares it to the parent recorded when the transaction started. A mismatch means the directory now reachable at that path is not the same one bound earlier (renamed, recreated, or reached through a changed symlink such as the prefix itself), so the pending rename/remove against the held fd is refused — a TOCTOU guard on generic artifact backup/rename operations.
Solutions
- Re-run the cask operation once the concurrent change has settled
- Stop overlapping mise/brew runs against the same prefix — serialize them with a lock
- If you deliberately changed a prefix symlink, finish or uninstall pending casks first, then retry
- If it recurs without concurrency, audit what renames the printed parent path (cleanup daemons, overlay fs) and report it
Example fix
# before: concurrent mutation $ brew reinstall foo & mise brew install --cask bar & # after: serialized $ brew reinstall foo && mise brew install --cask bar
Defensive patterns
Strategy: retry
Validate before calling
// Snapshot the parent identity before the operation and compare after. let before = std::fs::canonicalize(parent_dir)?; // ... run install ... let after = std::fs::canonicalize(parent_dir)?; assert_eq!(before, after, "parent changed during operation");
Try / catch
match run_install() {
Err(e) if e.to_string().contains("changed generic artifact parent") => {
// directory was retargeted mid-flight: settle the environment, retry
retry_after_concurrency_settles()
}
other => other,
} Prevention
- Don't rename or retarget prefix/appdir symlinks while installs are running
- Serialize package-manager mutations of the prefix
- Re-run the operation after any intentional prefix change
When it happens
Trigger: The artifact parent directory or a symlinked ancestor (the prefix) is renamed or retargeted while an install transaction is in flight; concurrent mise/brew operations restructuring the prefix subtree between the trusted walk and the mutation.
Common situations: Two package managers mutating /opt/homebrew concurrently; a prefix migration happening mid-operation; parallel CI jobs racing cask installs against the same prefix.
Related errors
- brew-cask: refusing operation through a changed generic…
- brew-cask: refusing elevated operation because target…
- brew-cask: temporary artifact directory was replaced
- brew-cask: temporary artifact directory was replaced
- created path component
AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22).
Data as JSON: /api/errors/1e65b4aedd59bf63.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/packages/brew/cask.rs:2602
.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 6f52dcdf99)