{"record":{"id":"e33a37bb8c20b6ba","repo":"jdx/mise","slug":"every-selected-bottle-was-prepared","errorCode":null,"errorMessage":"every selected bottle was prepared","messagePattern":"every selected bottle was prepared","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/mod.rs","lineNumber":301,"sourceCode":"                            unreachable!(\"every selected bottle has a preparation job\");\n                        };\n                        match result {\n                            Ok(bottle) => {\n                                prepared.insert(completed, bottle);\n                            }\n                            Err(err) => {\n                                reports[completed]\n                                    .finish_with_icon(\"failed\".to_string(), ProgressIcon::Error);\n                                failure = Some((completed, err));\n                            }\n                        }\n                    }\n                    if failure.is_some() {\n                        break;\n                    }\n                    let bottle = prepared\n                        .remove(&index)\n                        .expect(\"every selected bottle was prepared\");\n                    pour::install_prepared(bottle, &**pr).map(|()| pkg_version.clone())\n                }\n                None => {\n                    if failure.is_some() {\n                        break;\n                    }\n                    // Source builds must remain dependency ordered, but they\n                    // can safely run alongside bottle download/preparation,\n                    // which does not mutate active prefix links.\n                    let build = source::build(rf, &closure, &**pr);\n                    tokio::pin!(build);\n                    let mut jobs_open = true;\n                    loop {\n                        tokio::select! {\n                            // Record a ready bottle failure before a simultaneously\n                            // completed source build can advance the install loop.\n                            biased;\n                            job = bottle_jobs.next(), if jobs_open => match job {","sourceCodeStart":283,"sourceCodeEnd":319,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/packages/brew/mod.rs#L283-L319","documentation":"During Homebrew package install/upgrade via `pour`, bottles are prepared into a map keyed by package index; the install loop then `remove(&index).expect(\"every selected bottle was prepared\")`. The invariant is that every selected index has a prepared bottle; a panic means the preparation loop skipped or failed an index that the install loop still expects, i.e. state divergence between the two phases.","triggerScenarios":"Calling `install`/`upgrade` (src/system/packages/brew/mod.rs:301) where a bottle for a selected package index was never inserted into `prepared` — e.g. preparation short-circuited, a filter dropped an entry, or indices shifted between selection and preparation.","commonSituations":"Mixed package lists where some entries are casks/formulae filtered out during preparation but still present in the selection; a failed prep that doesn't remove the selection; concurrency changes altering index mapping.","solutions":["Update this code so a missing prepared bottle is reported as an error instead of panicking (record failure like the surrounding `failure` logic does)","Verify the preparation loop inserts an entry for every selected index even on failure paths","Reproduce with the exact package list and check which index is missing from `prepared`"],"exampleFix":"// before\nlet bottle = prepared.remove(&index).expect(\"every selected bottle was prepared\");\n// after\nlet Some(bottle) = prepared.remove(&index) else {\n    failure = Some(anyhow!(\"bottle for {} was not prepared\", pkg_version.name));\n    break;\n};","handlingStrategy":"try-catch","validationCode":"// before install: confirm selection and prepared keys align\nassert!(selection_indices.iter().all(|i| prepared.contains_key(i)), \"unprepared bottle selected\");","typeGuard":null,"tryCatchPattern":"let Some(bottle) = prepared.remove(&index) else {\n    failure = Some(anyhow!(\"bottle not prepared for index {index}\"));\n    break;\n};","preventionTips":["Ensure every selected index gets an entry in prepared, even on prep failure","Track failures per index instead of dropping entries","Add an integration test with a mixed formula/cask package list"],"tags":["panic","invariant","homebrew","install"],"backgroundTag":"internal-invariant-violation","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"}