{"record":{"id":"acf5713861004bfd","repo":"jdx/mise","slug":"metadata-only-app-ownership-cannot-be-proven-safel","errorCode":null,"errorMessage":"metadata-only app ownership cannot be proven safely during pruning","messagePattern":"metadata-only app ownership cannot be proven safely during pruning","errorType":"validation","errorClass":"eyre::Report","httpStatus":null,"severity":"warning","filePath":"src/system/packages/brew/cask.rs","lineNumber":7260,"sourceCode":"            );\n        }\n    }\n    Ok(())\n}\n\nfn validate_cask_prune_candidate(candidate: &CaskPruneCandidate) -> Result<()> {\n    if homebrew_metadata_present(&candidate.token) {\n        bail!(\"Homebrew now owns this cask\");\n    }\n    let receipt = &candidate.receipt;\n    if read_receipt(&candidate.version_dir)?.as_ref() != Some(receipt) {\n        bail!(\"ownership receipt has changed\");\n    }\n    if receipt.schema_version != 3 || !receipt.prune_safe || !receipt.pkg_ids.is_empty() {\n        bail!(\"receipt is not marked safe for direct-artifact pruning\");\n    }\n    if !receipt.metadata_only_apps.is_empty() {\n        bail!(\"metadata-only app ownership cannot be proven safely during pruning\");\n    }\n    let records = receipt\n        .targets\n        .iter()\n        .map(|record| (record.path.clone(), record))\n        .collect::<BTreeMap<_, _>>();\n    let expected = receipt\n        .apps\n        .iter()\n        .chain(&receipt.binaries)\n        .chain(&receipt.fonts)\n        .chain(&receipt.completions)\n        .cloned()\n        .collect::<BTreeSet<_>>();\n    if expected.is_empty()\n        || records.len() != receipt.targets.len()\n        || records.len() != expected.len()\n        || receipt","sourceCodeStart":7242,"sourceCodeEnd":7278,"githubUrl":"https://github.com/jdx/mise/blob/6f52dcdf99e282ef7a7db68c81301fa4618d0f79/src/system/packages/brew/cask.rs#L7242-L7278","documentation":"The receipt lists metadata_only_apps — app artifacts recorded as metadata-only because they were present on disk at install time but never staged or owned by the installer. Their bytes were never fingerprinted from a staged copy, so ownership cannot be proven at prune time. mise refuses the whole direct-artifact prune rather than risk deleting an app bundle it did not install.","triggerScenarios":"Pruning a cask whose install-time receipt has a non-empty metadata_only_apps list — e.g. the target .app path already existed in /Applications when mise poured the cask, so it was recorded as metadata-only instead of an owned staged artifact.","commonSituations":"Reinstalling a cask over an app the user placed in /Applications manually; casks whose declared apps ship with macOS or another package; leftover apps from a previous uninstall that were never removed.","solutions":["Remove the metadata-only apps manually (drag to Trash) and uninstall the cask through a non-prune path.","Delete the pre-existing app copies, reinstall the cask so mise stages and fingerprints them (making the receipt fully owned), then prune.","Accept the skip: the cask stays installed and is reported in the skipped list; remove it via `brew uninstall --cask` if Homebrew manages it."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Refuse to plan a direct prune when metadata-only apps are recorded\nif !receipt.metadata_only_apps.is_empty() {\n    // direct artifact pruning cannot prove ownership; skip or use manual uninstall\n}","typeGuard":null,"tryCatchPattern":"Convert the Err into a skip entry (token + reason) in the prune plan; surface it to the user as 'retained, remove manually'.","preventionTips":["Don't pre-create app bundles in /Applications that a cask will install.","After removing stray duplicate apps, reinstall the cask so the receipt records only owned targets."],"tags":["homebrew","cask","prune","ownership","receipt"],"backgroundTag":"unsafe-prune-refusal","analyzedSha":"6f52dcdf99e282ef7a7db68c81301fa4618d0f79","analyzedAt":"2026-08-22T10:14:23.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}