jdx/mise · error
brew-cask: generic artifact target
Error message
brew-cask: generic artifact target '{}' must stay below {} What it means
generic_artifact_target_path parses a cask artifact's target string after substituting $HOMEBREW_PREFIX with the active prefix. The result must be absolute, start with the prefix, contain at least one component below it, contain no ParentDir ('..') component, and pass path_starts_with_resolved_root so symlinked segments cannot route the real destination outside the prefix. Violations abort before anything is staged or copied.
Solutions
- Use an absolute target strictly below the active prefix, e.g. $HOMEBREW_PREFIX/etc/foo.conf
- Use the literal $HOMEBREW_PREFIX spelling for substitution — ${HOMEBREW_PREFIX} is not expanded
- Strip any '..' segments and ensure at least one component follows the prefix
- Verify the prefix mise resolves (HOMEBREW_PREFIX, mise settings) matches the cask's assumption
Example fix
# before artifact "lib/libfoo.dylib", target: "/usr/local/lib/libfoo.dylib" # after artifact "lib/libfoo.dylib", target: "$HOMEBREW_PREFIX/lib/libfoo.dylib"
Defensive patterns
Strategy: validation
Validate before calling
// Validate cask artifact target strings before running the install.
fn valid_generic_target(target: &str, prefix: &Path) -> bool {
let expanded = target.replace("$HOMEBREW_PREFIX", &prefix.to_string_lossy());
let p = Path::new(&expanded);
p.is_absolute()
&& p.starts_with(prefix)
&& p.strip_prefix(prefix).map(|r| r.components().next().is_some()).unwrap_or(false)
&& !p.components().any(|c| matches!(c, std::path::Component::ParentDir))
} Type guard
fn is_expanded_absolute_under_prefix(target: &str, prefix: &Path) -> bool {
valid_generic_target(target, prefix)
} Prevention
- Use the exact $HOMEBREW_PREFIX token (not ${HOMEBREW_PREFIX}) when relying on substitution
- Never hardcode /usr/local in casks that must also run on arm64 (/opt/homebrew)
- Keep targets at least one component below the prefix and free of '..'
When it happens
Trigger: artifact target: "/etc/foo" or "~/Library/x" (not under the prefix); "$HOMEBREW_PREFIX" alone; "$HOMEBREW_PREFIX/opt/../etc/x"; a target under a symlink that resolves outside the prefix; unexpanded ${HOMEBREW_PREFIX} spelling (only the bare $HOMEBREW_PREFIX form is replaced).
Common situations: Casks ported from Homebrew ruby that install to system paths; hardcoded /usr/local targets on arm64 machines whose prefix is /opt/homebrew; templating that emits relative paths or unexpanded variables.
Related errors
- brew-cask: refusing generic artifact copy outside Homebrew…
- brew-cask: invalid generic artifact parent
- brew-cask: refusing to remove generic artifact outside
- brew-cask: ' ' must be an absolute path
- brew-cask: ' ' must not resolve to the filesystem root
AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22).
Data as JSON: /api/errors/a0117444eb7ace2f.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/packages/brew/cask.rs:2501
run_installer_artifact(stage, installer, targets.copied_files())?;
completed(index)?;
}
durabilize_staged_symlink_targets(stage, temporary_caskroom, targets)
}
fn generic_artifact_target_path(target: &str) -> Result<PathBuf> {
let prefix = prefix::prefix();
let expanded = target.replace("$HOMEBREW_PREFIX", &prefix.to_string_lossy());
let target = PathBuf::from(expanded);
if !target.is_absolute()
|| !target.starts_with(&prefix)
|| target.strip_prefix(&prefix)?.components().next().is_none()
|| target
.components()
.any(|component| matches!(component, Component::ParentDir))
|| !path_starts_with_resolved_root(&target, &prefix)
{
bail!(
"brew-cask: generic artifact target '{}' must stay below {}",
target.display(),
prefix.display()
);
}
Ok(target)
}
fn generic_artifact_targets(artifacts: &CaskArtifacts) -> Result<Vec<PathBuf>> {
artifacts
.generic
.iter()
.map(|artifact| generic_artifact_target_path(&artifact.target))
.collect()
}
fn previous_generic_targets(cask: &Cask) -> Result<Vec<CaskTargetRecord>> {
let Some(version) = installed_version(&cask.token) else {View on GitHub (pinned to 6f52dcdf99)