{"record":{"id":"3a6bb55c8079ac34","repo":"Universal-Debloater-Alliance/universal-android-debloater-next-generation","slug":"removal-recommendation-must-be-selected","errorCode":null,"errorMessage":"removal recommendation must be selected","messagePattern":"removal recommendation must be selected","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/uad-gui/src/views/list.rs","lineNumber":749,"sourceCode":"                .spacing(10)\n                .align_x(Alignment::Center)\n            },\n        )\n        .width(900)\n        .height(Length::Shrink)\n        .max_height(700)\n        .style(style::Container::Background)\n        .into()\n    }\n\n    fn filter_package_lists(&mut self) {\n        let list_filter: UadList = self.selected_list.expect(\"UAD-list type must be selected\");\n        let package_filter: PackageState = self\n            .selected_package_state\n            .expect(\"pack-state must be selected\");\n        let removal_filter: Removal = self\n            .selected_removal\n            .expect(\"removal recommendation must be selected\");\n\n        self.filtered_packages = self.phone_packages\n            [self.selected_user.expect(\"User must be selected\").index]\n            .iter()\n            // we must filter the indices associated with pack-rows,\n            // that's why `enumerate` is before `filter`.\n            .enumerate()\n            .filter(|(_, p)| {\n                (list_filter == UadList::All || p.list == list_filter)\n                    && (package_filter == PackageState::All || p.state == package_filter)\n                    && (removal_filter == Removal::All || p.removal == removal_filter)\n                    && (self.input_value.is_empty()\n                        || matches_search(&p.name, &self.input_value, Some(&p.description)))\n            })\n            .map(|(i, _)| i)\n            .collect();\n    }\n","sourceCodeStart":731,"sourceCodeEnd":767,"githubUrl":"https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/64465c850c7ed36329e67165ac08501abffb218e/crates/uad-gui/src/views/list.rs#L731-L767","documentation":"This is a Rust `Option::expect` panic in the `uad-gui` Iced application. `filter_package_lists` unwraps `self.selected_removal` (the currently chosen removal recommendation filter, of type `Option<Removal>`), panicking with 'removal recommendation must be selected' if it is `None`. The invariant assumes the removal dropdown always has a selection before packages are filtered; if initialization or the message that resets selections runs first, the `None` case is hit.","triggerScenarios":"Calling `filter_package_lists` (e.g. via the List view's filter-related Iced messages) before `self.selected_removal` is ever assigned, or after a code path resets it to `None` (e.g. on user switch or list reload) while a filter refresh is still triggered.","commonSituations":"App startup ordering where the packages view refreshes before the dropdown defaults are set; a refactor that makes `selected_removal` optional or resets it to `None` on a user change; a message dispatched from a background task completing after the view state was cleared.","solutions":["Initialize `selected_removal: Option<Removal>` to `Some(Removal::Recommended)` (or the desired default) in the view's `new`/`default` constructor instead of `None`","Replace `.expect(...)` with `.unwrap_or(Removal::Recommended)` or an `if let Some(removal_filter) = self.selected_removal { ... }` early return that skips filtering when unset","Ensure every code path that clears `selected_user`/view state also re-seeds `selected_removal` before triggering `filter_package_lists`","Add a guard at the top of `filter_package_lists`: `let Some(removal_filter) = self.selected_removal else { return; };`"],"exampleFix":"// before\nlet removal_filter: Removal = self\n    .selected_removal\n    .expect(\"removal recommendation must be selected\");\n// after\nlet removal_filter: Removal = self\n    .selected_removal\n    .unwrap_or(Removal::Recommended);","handlingStrategy":"validation","validationCode":"if self.selected_removal.is_none() {\n    // skip filtering or apply default\n    return;\n}","typeGuard":"fn removal_selected(state: &ListView) -> Option<Removal> {\n    state.selected_removal\n}","tryCatchPattern":"// Rust panics are not catchable idiomatically; instead:\nlet removal_filter = self.selected_removal.unwrap_or(Removal::Recommended);","preventionTips":["Seed every Option filter field with a sensible default in the view constructor instead of None","Prefer unwrap_or/default over expect for UI dropdown state","Audit all state-reset paths (user switch, device change) to re-seed selections","Run integration tests that dispatch filter messages before user interactions"],"tags":["rust","panic","unwrap-none","gui-state","iced"],"backgroundTag":"internal-invariant-violation","analyzedSha":"64465c850c7ed36329e67165ac08501abffb218e","analyzedAt":"2026-09-12T09:09:23.137Z","contentChangedAt":"2026-09-12T09:09:23.137Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}