jdx/mise · critical
brew-cask: refusing elevated operation through mutable…
Error message
brew-cask: refusing elevated operation through mutable directory {} What it means
validate_strict_elevated_ancestors (src/system/packages/brew/cask.rs:2010) walks every ancestor of a path a sudo operation will write through and requires each to be trusted: root-owned (uid 0), not world-writable, and group-writable only for the exact Homebrew prefix itself (the Intel root:admin 0775 /usr/local exception in strict_elevated_directory_is_trusted). Any mutable directory — user-owned, group-writable, or reached via untrusted symlinks — aborts the privileged copy.
Solutions
- Install to the default locations mise/Homebrew use — root-owned ancestors with no group/world write — instead of custom writable targets
- Tighten the offending directory named in the error: sudo chown root <dir> && sudo chmod go-w <dir>
- Replace user-owned symlinks in the ancestor chain with real directories (desymlink the path)
- If the dir is Intel brew's 0775 prefix, ensure it is exactly the configured brew prefix — the exception applies only there
Example fix
# before $ sudo chmod -R g+w /opt/tools # ancestors became group-writable $ mise install brew-cask:foo # refuses elevated operation through /opt/tools # after $ sudo chown root /opt/tools && sudo chmod go-w /opt/tools $ mise install brew-cask:foo
Defensive patterns
Strategy: validation
Validate before calling
for dir in target.ancestors() {
let md = dir.symlink_metadata()?;
let trusted = md.uid() == 0 && md.mode() & 0o002 == 0;
if !trusted {
return Err(anyhow!("mutable ancestor {} - refusing elevated operation", dir.display()));
}
} Prevention
- Install cask artifacts to default root-owned locations, not custom user-writable trees
- chmod go-w every directory a sudo copy traverses
- Replace user-owned symlinks in target paths with real directories before installing
When it happens
Trigger: A cask artifact target whose path traverses a user-owned or group/world-writable directory: custom targets pointing into /Users/.../bin or a chmod 0777/0775 dir, or symlinked components resolving into untrusted locations during the ancestor walk.
Common situations: Custom artifact target paths in cask stanzas on shared machines; directories loosened to 0775/0777 for convenience; Intel Macs where /usr/local subdirectories were chowned to the admin user; relocated brew prefixes outside the trusted prefix.
Related errors
- brew-cask: temporary artifact directory is not private
- brew-cask: refusing elevated operation through mutable…
- brew-cask: refusing operation through untrusted directory
- brew-cask: refusing operation through untrusted directory
- brew-cask: staging directory is not owned by the current…
AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22).
Data as JSON: /api/errors/0646ed89044d1755.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/packages/brew/cask.rs:2010
target
.file_name()
.ok_or_else(|| eyre!("brew-cask: generic artifact target has no filename"))?,
))
}
#[cfg(unix)]
fn validate_strict_elevated_ancestors(path: &Path) -> Result<()> {
use std::os::unix::fs::MetadataExt;
let stable_prefix = file::desymlink_path(&prefix::prefix());
for directory in path.ancestors() {
let metadata = directory.symlink_metadata()?;
if !strict_elevated_directory_is_trusted(
directory,
&stable_prefix,
metadata.uid(),
metadata.mode(),
) {
bail!(
"brew-cask: refusing elevated operation through mutable directory {}",
directory.display()
);
}
}
Ok(())
}
#[cfg(unix)]
fn strict_elevated_directory_is_trusted(
directory: &Path,
stable_prefix: &Path,
uid: u32,
mode: u32,
) -> bool {
uid == 0
&& mode & 0o002 == 0
// Intel Homebrew conventionally uses root:admin 0775 for /usr/local.View on GitHub (pinned to 6f52dcdf99)