{"record":{"id":"0ce30d15ea548d94","repo":"jdx/mise","slug":"brew-cask-refusing-to-remove-generic-artifact-out-0ce30d","errorCode":null,"errorMessage":"brew-cask: refusing to remove generic artifact outside {}: {}","messagePattern":"brew-cask: refusing to remove generic artifact outside (.+?): (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/cask/mod.rs","lineNumber":2478,"sourceCode":"    };\n    Ok(receipt\n        .targets\n        .into_iter()\n        .filter(|record| receipt.generic.contains(&record.path))\n        .collect())\n}\n\nfn remove_obsolete_generic_artifacts(\n    previous_targets: &[CaskTargetRecord],\n    current_targets: &[PathBuf],\n) -> Result<()> {\n    let prefix = prefix::prefix();\n    for record in previous_targets {\n        if current_targets.contains(&record.path) || !cask_target_record_matches(record)? {\n            continue;\n        }\n        if !path_starts_with_resolved_root(&record.path, &prefix) {\n            bail!(\n                \"brew-cask: refusing to remove generic artifact outside {}: {}\",\n                prefix.display(),\n                record.path.display()\n            );\n        }\n        if let Err(err) = remove_trusted_generic_target(&record.path) {\n            warn!(\n                \"brew-cask: leaving obsolete generic artifact {} because its parent directories are mutable: {err:#}\",\n                record.path.display()\n            );\n        }\n    }\n    Ok(())\n}\n\nfn remove_trusted_generic_target(target: &Path) -> Result<()> {\n    let expected_parent = resolved_parent(target)?;\n    match remove_trusted_generic_target_from(target, &expected_parent) {","sourceCodeStart":2460,"sourceCodeEnd":2496,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/packages/brew/cask/mod.rs#L2460-L2496","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: record points to absolute legacy path\n/usr/local/share/mytool/data.bin (recorded, prefix now /opt/homebrew)\n// after: remove manually or relink under current prefix\nrm /usr/local/share/mytool/data.bin   # then: mise install of cask proceeds","handlingStrategy":"try-catch","validationCode":"let resolved = std::fs::canonicalize(&record.path)?;\nif !resolved.starts_with(&prefix) {\n    eprintln!(\"stale artifact outside prefix, remove manually: {}\", record.path.display());\n}","typeGuard":"fn record_inside_prefix(p: &std::path::Path, prefix: &std::path::Path) -> bool {\n    std::fs::canonicalize(p).map(|p| p.starts_with(prefix)).unwrap_or(false)\n}","tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"refusing to remove generic artifact\") => {\n        // remove the out-of-prefix file manually, then retry\n    }\n    r => r?,\n}","preventionTips":["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."],"tags":["brew-cask","uninstall","prefix","path-safety"],"backgroundTag":"path-traversal-blocked","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}