{"record":{"id":"bcce9541b7eb9bb6","repo":"jdx/mise","slug":"receipt-target-inventory-is-incomplete-or-duplicat","errorCode":null,"errorMessage":"receipt target inventory is incomplete or duplicated","messagePattern":"receipt target inventory is incomplete or duplicated","errorType":"validation","errorClass":"eyre::Report","httpStatus":null,"severity":"warning","filePath":"src/system/packages/brew/cask.rs","lineNumber":7283,"sourceCode":"        .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\n            .metadata_only_apps\n            .iter()\n            .any(|path| !receipt.apps.contains(path))\n    {\n        bail!(\"receipt target inventory is incomplete or duplicated\");\n    }\n    if records.keys().any(|path| !expected.contains(path)) {\n        bail!(\"receipt target inventory contains an unclassified path\");\n    }\n\n    for path in &receipt.apps {\n        let record = records\n            .get(path)\n            .ok_or_else(|| eyre!(\"missing app target record\"))?;\n        if record.fingerprint.kind != CaskTargetKind::Directory\n            || !allowed_appdir_roots()?\n                .iter()\n                .any(|root| path_is_below(path, root))\n            || !path.file_name().is_some_and(|name| {\n                staged_app_matches_target(record, &candidate.version_dir.join(name))\n            })\n        {\n            bail!(","sourceCodeStart":7265,"sourceCodeEnd":7301,"githubUrl":"https://github.com/jdx/mise/blob/6f52dcdf99e282ef7a7db68c81301fa4618d0f79/src/system/packages/brew/cask.rs#L7265-L7301","documentation":"The receipt's classified artifact lists (apps, binaries, fonts, completions) are cross-checked against receipt.targets: the expected set must be non-empty, the deduplicated target-record map must equal receipt.targets in size (no duplicate paths), the record count must equal the expected set size (every classified path has exactly one record), and every metadata_only_apps entry must appear in apps. Any inconsistency means the receipt is internally incomplete or duplicated, so the prune is refused.","triggerScenarios":"A receipt where the same path appears twice in targets; a classified path (in apps/binaries/fonts/completions) with no corresponding target record; an empty artifact set; a metadata_only_apps path not listed in apps. Produced by receipt-writer bugs or a corrupted `.mise-cask.toml`.","commonSituations":"Receipts written by an older/newer mise with a different layout; truncated or corrupt TOML after a crash mid-write; hand-edited receipts.","solutions":["Reinstall the cask with the current mise to regenerate a self-consistent receipt, then prune.","Inspect `<Caskroom>/<token>/<version>/.mise-cask.toml` for duplicate or missing target entries if manual repair is acceptable.","If it reproduces on a fresh install, file a mise bug with the receipt contents."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Mirror the receipt cross-check before planning a prune\nuse std::collections::{BTreeMap, BTreeSet};\nfn inventory_consistent(r: &CaskReceipt) -> bool {\n    let expected: BTreeSet<_> = r.apps.iter().chain(&r.binaries).chain(&r.fonts).chain(&r.completions).cloned().collect();\n    let records: BTreeMap<_, _> = r.targets.iter().map(|t| (t.path.clone(), t)).collect();\n    !expected.is_empty()\n        && records.len() == r.targets.len()\n        && records.len() == expected.len()\n        && r.metadata_only_apps.iter().all(|p| r.apps.contains(p))\n}","typeGuard":null,"tryCatchPattern":"Wrap validate_cask_prune_candidate in `if let Err(reason)` and record a skip; treat any inconsistency as 'receipt unusable — reinstall to regenerate'.","preventionTips":["Regenerate receipts by reinstalling after mise version changes instead of pruning old receipts.","Never hand-edit receipt TOML; a single duplicate path invalidates pruning."],"tags":["homebrew","cask","prune","receipt","integrity"],"backgroundTag":"manifest-mismatch","analyzedSha":"6f52dcdf99e282ef7a7db68c81301fa4618d0f79","analyzedAt":"2026-08-22T10:14:23.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}