jdx/mise · error
brew-cask: refusing to remove generic artifact outside
Error message
brew-cask: refusing to remove generic artifact outside {}: {} What it means
During cask upgrade/removal, mise deletes previously installed generic artifacts that are no longer part of the current cask version. Before removing a recorded target, it verifies the recorded path still resolves inside the brew prefix; if a record points outside the prefix (e.g. because the prefix changed or the path was tampered with), mise refuses to delete it rather than removing arbitrary user files.
Solutions
- Manually remove the out-of-prefix artifact listed in the message, then re-run the cask uninstall/upgrade.
- Reinstall the cask under the current prefix so target records are regenerated with valid paths.
- Fix symlinks at the recorded path that resolve outside the prefix (relink them inside or delete them).
- Ensure HOMEBREW_PREFIX / mise prefix configuration matches what was used when the artifacts were installed.
- Inspect and clean stale target records from the previous install before upgrading.
Example fix
// before: record points to absolute legacy path /usr/local/share/mytool/data.bin (recorded, prefix now /opt/homebrew) // after: remove manually or relink under current prefix rm /usr/local/share/mytool/data.bin # then: mise install of cask proceeds
Defensive patterns
Strategy: try-catch
Validate before calling
let resolved = std::fs::canonicalize(&record.path)?;
if !resolved.starts_with(&prefix) {
eprintln!("stale artifact outside prefix, remove manually: {}", record.path.display());
} Type guard
fn record_inside_prefix(p: &std::path::Path, prefix: &std::path::Path) -> bool {
std::fs::canonicalize(p).map(|p| p.starts_with(prefix)).unwrap_or(false)
} Try / catch
match result {
Err(e) if e.to_string().contains("refusing to remove generic artifact") => {
// remove the out-of-prefix file manually, then retry
}
r => r?,
} Prevention
- Keep a stable brew prefix; avoid migrating /usr/local to /opt/homebrew without reinstalling casks.
- Do not replace managed artifact paths with external symlinks.
- Reinstall casks after prefix changes so target records refresh.
- Audit stale target records before upgrades.
When it happens
Trigger: Uninstalling or upgrading a cask when a recorded generic-artifact target from a previous install resolves outside the current prefix; the brew prefix was changed or migrated between installs; the target path was replaced by a symlink escaping the prefix; stale/corrupted target records.
Common situations: Users migrating Homebrew from /usr/local to /opt/homebrew (or custom prefixes) then upgrading casks; manually symlinked artifact targets; leftover records from an older mise/brew layout.
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: generic artifact target
- brew-cask: generic artifact backup changed directories
- brew-cask: refusing to restore flight target through a…
- app target is outside an allowed Applications directory
- brew-cask: ' ' must be an absolute path
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/0ce30d15ea548d94.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/packages/brew/cask/mod.rs:2478
};
Ok(receipt
.targets
.into_iter()
.filter(|record| receipt.generic.contains(&record.path))
.collect())
}
fn remove_obsolete_generic_artifacts(
previous_targets: &[CaskTargetRecord],
current_targets: &[PathBuf],
) -> Result<()> {
let prefix = prefix::prefix();
for record in previous_targets {
if current_targets.contains(&record.path) || !cask_target_record_matches(record)? {
continue;
}
if !path_starts_with_resolved_root(&record.path, &prefix) {
bail!(
"brew-cask: refusing to remove generic artifact outside {}: {}",
prefix.display(),
record.path.display()
);
}
if let Err(err) = remove_trusted_generic_target(&record.path) {
warn!(
"brew-cask: leaving obsolete generic artifact {} because its parent directories are mutable: {err:#}",
record.path.display()
);
}
}
Ok(())
}
fn remove_trusted_generic_target(target: &Path) -> Result<()> {
let expected_parent = resolved_parent(target)?;
match remove_trusted_generic_target_from(target, &expected_parent) {View on GitHub (pinned to 533346cc37)