jdx/mise · error
brew-cask: refusing generic artifact copy outside Homebrew p
Error message
brew-cask: refusing generic artifact copy outside Homebrew prefix: {} What it means
Generic artifacts (plain files a cask copies into place) must be installed strictly inside the Homebrew prefix. validate_generic_copy_target rejects a target that is not under $HOMEBREW_PREFIX, that is the prefix itself (no component beneath it), or whose symlink-resolved root no longer sits under the prefix (path_starts_with_resolved_root). The refusal prevents a cask from writing outside the managed prefix.
Source
Thrown at src/system/packages/brew/cask.rs:2063
)?;
if bound.st_dev != linked.st_dev || bound.st_ino != linked.st_ino {
bail!("brew-cask: temporary artifact directory was replaced");
}
nix::unistd::unlinkat(
&parent.fd,
staging_name,
nix::unistd::UnlinkatFlags::RemoveDir,
)?;
Ok(())
}
fn validate_generic_copy_target(target: &Path) -> Result<()> {
let prefix = prefix::prefix();
if !target.starts_with(&prefix)
|| target.strip_prefix(&prefix)?.components().next().is_none()
|| !path_starts_with_resolved_root(target, &prefix)
{
bail!(
"brew-cask: refusing generic artifact copy outside Homebrew prefix: {}",
target.display()
);
}
Ok(())
}
#[cfg(unix)]
struct TrustedOperationParent {
fd: std::os::fd::OwnedFd,
}
#[cfg(unix)]
impl TrustedOperationParent {
fn path(&self) -> Result<PathBuf> {
#[cfg(target_os = "linux")]
return Ok(
Path::new("/proc/self/fd").join(std::os::fd::AsRawFd::as_raw_fd(&self.fd).to_string())View on GitHub (pinned to 9dcfcaa0dc)
Solutions
- Fix the cask artifact stanza so the target is an absolute path under $HOMEBREW_PREFIX (e.g. "$HOMEBREW_PREFIX/etc/foo.conf")
- Verify HOMEBREW_PREFIX is set and matches the installation being operated on
- Resolve symlinked prefix components (use the realpath of $(brew --prefix)) or replace them with real directories
- If the file must genuinely live outside the prefix, install it with a different mechanism: generic artifacts cannot escape the prefix
Example fix
# cask artifact stanza -- before target: "/etc/mytool.conf" # after target: "$HOMEBREW_PREFIX/etc/mytool.conf"
Defensive patterns
Strategy: validation
Validate before calling
use std::path::{Path, Component};
fn generic_target_is_valid(target: &Path, prefix: &Path) -> bool {
target.is_absolute()
&& target.starts_with(prefix)
&& target.strip_prefix(prefix).is_ok_and(|r| r.components().next().is_some())
&& !target.components().any(|c| matches!(c, Component::ParentDir))
&& std::fs::canonicalize(target).ok()
.zip(std::fs::canonicalize(prefix).ok())
.is_some_and(|(t, p)| t.starts_with(p))
} Type guard
fn is_beneath_homebrew_prefix(target: &std::path::Path) -> bool {
let prefix = homebrew_prefix();
target.starts_with(&prefix)
&& target.strip_prefix(&prefix).is_ok_and(|r| r.components().next().is_some())
} Try / catch
match copy_generic_artifact(&artifact) {
Err(e) if e.to_string().contains("outside Homebrew prefix") => {
// rewrite the target under $HOMEBREW_PREFIX in the cask stanza, then retry
return Err(e.wrap_err("target must live under $HOMEBREW_PREFIX"));
}
other => other?,
} Prevention
- Always express generic artifact targets with the $HOMEBREW_PREFIX variable, never absolute system paths
- Keep HOMEBREW_PREFIX stable across installs and upgrades
- Avoid symlinking the prefix itself
When it happens
Trigger: A cask artifact target that resolves to /etc, /Library, ~/, or any path outside $HOMEBREW_PREFIX; a target equal to the prefix with nothing beneath it; a target lexically inside the prefix but reachable through a symlink that resolves elsewhere.
Common situations: HOMEBREW_PREFIX changed or differing from what the cask assumed; prefix path components replaced by symlinks (custom installs like ~/homebrew); casks ported from formula that hardcode absolute system paths.
Related errors
- brew-cask: generic artifact target '{}' must stay below {}
- brew-cask: refusing to remove generic artifact outside {}: {
- mise upgrade --monorepo is not implemented yet
- brew-cask: invalid generic artifact parent
- brew-cask: refusing installer executable outside trusted ins
AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17).
Data as JSON: /api/errors/69fd493e898b1bf4.
Report an issue: GitHub.