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
Rename/unlink helpers for generic artifacts open the parent directory as a descriptor at transaction start and, right before acting, canonicalize the descriptor's path and compare it with the expected parent recorded earlier. A mismatch means the directory reachable at that name was replaced or moved mid-operation, so the rename is refused rather than performed through an unexpected directory.
Source
Thrown at src/system/packages/brew/cask.rs:2476
.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 9dcfcaa0dc)
Solutions
- Retry the cask operation once no other process is modifying that parent directory
- Remove and recreate the parent directory cleanly (with correct ownership), then re-run the install
- Serialize automation so only one installer manipulates the target tree at a time
Defensive patterns
Strategy: retry
Validate before calling
use std::fs;
// Before the operation, confirm the parent still resolves to the expected directory
// and is not world-writable (cheap pre-flight for the identity check)
fn parent_matches_expected(parent: &std::path::Path, expected: &std::path::Path) -> bool {
fs::canonicalize(parent).is_ok_and(|p| p == expected)
} Try / catch
match rename_trusted_generic_target(from, to, expected_parent) {
Err(e) if e.to_string().contains("changed generic artifact parent") => {
// the parent dir was replaced mid-run: recreate a clean parent and retry once
std::thread::sleep(std::time::Duration::from_secs(1));
rename_trusted_generic_target(from, to, expected_parent)?
}
other => other?,
} Prevention
- Serialize installers so only one process mutates a given parent directory
- Do not delete/recreate artifact parent directories while installs are in flight
- Re-run the cask operation after cleaning up a crashed attempt
When it happens
Trigger: The parent directory of a generic artifact is deleted and recreated, renamed, or re-symlinked between the start of the artifact operation and the rename/restore step; concurrent installers writing the same directory.
Common situations: Parallel package managers or manual file management touching ~/Applications or the prefix while a cask install runs; a crashed prior attempt leaving a replacement directory; automation scripts that rebuild directories mid-install.
Related errors
- brew-cask: temporary artifact directory was replaced
- brew-cask:{}: Homebrew took ownership of this cask while ins
- brew-cask: refusing elevated operation because target appear
- brew-cask: generic artifact backup changed directories
- brew-cask: refusing to restore flight target through a chang
AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17).
Data as JSON: /api/errors/1e65b4aedd59bf63.
Report an issue: GitHub.