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
`validate_generic_copy_target` enforces that every generic artifact copy destination lives inside the Homebrew prefix. It rejects targets that are not under `prefix::prefix()`, that resolve to the prefix itself (empty first component after strip_prefix), or whose resolved real path escapes the prefix via symlinks (`path_starts_with_resolved_root`). This is a path-escape guard so a malicious cask cannot write artifacts to arbitrary system locations via symlinked paths.
Source
Thrown at src/system/packages/brew/cask/mod.rs:1911
)?;
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 afd2eddd3a)
Solutions
- Check the printed target path against your actual prefix (`brew --prefix`) and correct the artifact destination to live inside it.
- Verify the Homebrew prefix configuration (HOMEBREW_PREFIX / install location) matches where the artifacts are expected; a mismatched prefix causes legitimate targets to be rejected.
- Remove intermediate symlinks between the target and the prefix, or reference the target via its real path under the prefix so the resolved-root check passes.
- If the cask genuinely must install outside the prefix, use the appropriate cask stanza/mechanism for that (not the generic copier), which has its own validation.
Example fix
// before: absolute destination outside the prefix
let target = Path::new("/usr/local/bin/mytool");
// after: derive destination from the prefix
let target = prefix::prefix().join("bin").join("mytool"); Defensive patterns
Strategy: validation
Validate before calling
use std::path::{Path, PathBuf};
fn target_inside_prefix(target: &Path, prefix: &Path) -> bool {
target.is_absolute()
&& target.starts_with(prefix)
&& target.strip_prefix(prefix).map(|r| r.components().next().is_some()).unwrap_or(false)
&& target.canonicalize().ok().filter(|r| r.starts_with(prefix)).is_some()
}
Type guard
fn is_safe_prefix_target(t: &Path, prefix: &Path) -> bool {
t.starts_with(prefix) && t.canonicalize().map(|c| c.starts_with(prefix)).unwrap_or(false)
}
Prevention
- Always derive artifact destinations from the brew prefix, never hardcode absolute paths.
- Verify HOMEBREW_PREFIX matches the actual install location.
- Avoid symlinked path components between the target and the prefix; use canonical paths.
When it happens
Trigger: A generic artifact copy whose `target` path (a) lies outside the Homebrew prefix, (b) equals the prefix itself with no remaining components, or (c) is a symlinked path that resolves outside the prefix — all reachable through any cask install/artifact-staging call that goes through `validate_generic_copy_target`.
Common situations: A cask artifact stanza with an absolute destination outside the brew prefix (e.g. `/usr/local/bin` on a non-Homebrew-managed layout); `HOMEBREW_PREFIX` pointing somewhere unexpected so valid destinations fall outside it; a symlinked prefix component (e.g. `/opt/homebrew` aliased through another path) making the resolved root differ from the lexical prefix.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- brew-cask: invalid {kind} '{value}'
- {option}: '{name}' must be a plain file name (no path separa
- {option}: '{path}' must be a safe relative path (no absolute
- invalid relay path
- brew-cask: invalid font target '{}'
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/0a3d92b84579eade.
Report an issue: GitHub.