jdx/mise · error
brew-cask: generic artifact source is not contained by the…
Error message
brew-cask: generic artifact source is not contained by the extraction root: {} What it means
During generic-artifact cask installation, mise locates the artifact inside the extraction stage and computes its path relative to the stage root. If the resolved source path cannot be expressed as a path strictly contained by the extraction root (including via resolved symlinks), installation aborts. This is a path-containment safety check that prevents a cask-defined `source` glob from matching files outside the extracted payload.
Solutions
- Update or fix the cask definition so `source` matches only paths inside the extracted artifact
- Re-download the cask payload to rule out a corrupted or altered stage
- Check for symlinked ancestors of the mise temp/stage directories and use a real path if a local workaround is needed
- Report the cask to the mise/homebrew maintainers if a stock cask triggers this
Example fix
// cask generic artifact before (source escapes the stage) // source: "/Applications/../.." // after // source: "App.app" (relative to extraction root)
Defensive patterns
Strategy: validation
Validate before calling
let stage = fs::canonicalize(stage_dir)?;
let source = fs::canonicalize(stage_dir.join(cask_source))?;
if !source.starts_with(&stage) {
return Err(format!("artifact source {source:?} escapes stage {stage:?}"));
} Type guard
fn is_contained(child: &Path, root: &Path) -> bool {
std::fs::canonicalize(child).map(|c| c.starts_with(root)).unwrap_or(false)
} Prevention
- Keep cask artifact sources relative to the extraction root
- Avoid symlinked temp directories when installing casks
- Re-fetch casks instead of hand-editing stanzas
When it happens
Trigger: Installing a cask with a `generic` artifact whose `source` resolves (after `find_artifact_matching` traverses symlinks) to a location outside the extraction stage; also triggered when the stage directory itself is behind a symlinked ancestor so lexical and resolved prefixes diverge and `staged_relative_path` fails against both.
Common situations: A cask stanza lists a source path that the package actually places outside the stage (mis-authored or changed upstream cask); a user's temp directory is a symlink (/tmp -> /private/tmp) interacting with stage path resolution; tampered or modified cask definitions.
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: app target
- brew-cask: binary target
- brew-cask: invalid generic artifact parent
- brew-cask: refusing generic artifact copy outside Homebrew…
- brew-cask: refusing installer executable outside trusted…
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/54b28c6179b62adf.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/packages/brew/cask/mod.rs:1791
"brew-cask: refusing generic artifact source outside the extraction root: {}",
source.display()
);
}
let target = generic_artifact_target_path(&artifact.target)?;
// Not a lexical `strip_prefix`: the lookup resolves symlinks it had to
// traverse, so a source reached that way can be contained by the stage
// without sharing its literal prefix — as it is whenever `stage` itself
// has a symlinked ancestor. `staged_relative_path` retries against the
// resolved stage, matching the containment check above.
let relative_source = staged_relative_path(stage, &source).ok_or_else(|| {
eyre!(
"brew-cask: generic artifact source is not contained by the extraction root: {}",
source.display()
)
})?;
let caskroom_source = temporary_caskroom.join(relative_source);
if !path_starts_with_resolved_root(&caskroom_source, temporary_caskroom) {
bail!(
"brew-cask: refusing to stage generic artifact through a path outside the caskroom: {}",
caskroom_source.display()
);
}
#[cfg(not(unix))]
if let Some(parent) = target.parent() {
file::create_dir_all(parent)?;
}
let elevated_target = targets.protect_generic(&target)?;
copy_generic_artifact(&source, &target, elevated_target.as_deref())?;
if let Some(parent) = caskroom_source.parent() {
file::create_dir_all(parent)?;
}
file::make_symlink(&target, &caskroom_source)?;
targets.record_installed(target);
Ok(())
}
View on GitHub (pinned to 533346cc37)