{"record":{"id":"47357238236e640b","repo":"Universal-Debloater-Alliance/universal-android-debloater-next-generation","slug":"user-must-be-selected","errorCode":null,"errorMessage":"User must be selected","messagePattern":"User must be selected","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/uad-gui/src/views/list.rs","lineNumber":752,"sourceCode":"        )\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\n    #[expect(clippy::unused_async, reason = \"1 call-site\")]\n    async fn load_packages<S: AsRef<str>>(\n        uad_list: PackageHashMap,","sourceCodeStart":734,"sourceCodeEnd":770,"githubUrl":"https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/64465c850c7ed36329e67165ac08501abffb218e/crates/uad-gui/src/views/list.rs#L734-L770","documentation":"This is a Rust `Option::expect` panic in `uad-gui`'s `filter_package_lists`. It indexes `self.phone_packages[self.selected_user.expect(\"User must be selected\").index]`, panicking when `self.selected_user` is `None`. The code assumes a user (ADB phone user with an `.index`) is always chosen before packages are filtered; if no user is selected the index lookup cannot proceed and the app panics instead of degrading gracefully.","triggerScenarios":"`filter_package_lists` runs while `self.selected_user` is `None`: before the user list is fetched/selected on first launch, after a device disconnect/reconnect clears the selection, or when a filter-change message is dispatched while `selected_user` was reset by a user-switch handler.","commonSituations":"First app start before the ADB user list loads; unplugging/replugging the phone clears the selected user but stale filter messages still fire; switching devices mid-session where the new device's users haven't been populated yet; a race between a background task message and view reset.","solutions":["Guard before indexing: `let Some(user) = self.selected_user else { self.filtered_packages.clear(); return; };`","Fall back to the first/default user: `let user = self.selected_user.as_ref().or_else(|| self.users.first())...`","Ensure the user-selection handler runs (and sets `selected_user`) before any message that triggers `filter_package_lists` is processed","Replace the expect with `.expect` removal plus bounds/None handling, e.g. `self.phone_packages.get(user.index).map(...)` returning empty vec on miss"],"exampleFix":"// before\nself.filtered_packages = self.phone_packages\n    [self.selected_user.expect(\"User must be selected\").index]\n    .iter()\n// after\nlet Some(user) = self.selected_user.as_ref() else {\n    self.filtered_packages.clear();\n    return;\n};\nself.filtered_packages = self.phone_packages\n    .get(user.index)\n    .map(|pkgs| pkgs.iter().collect::<Vec<_>>())\n    .unwrap_or_default();","handlingStrategy":"validation","validationCode":"if self.selected_user.is_none()\n    || self.phone_packages.get(self.selected_user.as_ref().unwrap().index).is_none()\n{\n    self.filtered_packages.clear();\n    return;\n}","typeGuard":"fn selected_user_index(state: &ListView) -> Option<usize> {\n    state.selected_user.as_ref().map(|u| u.index)\n}","tryCatchPattern":"// Prefer guard clause over catch:\nlet Some(user) = self.selected_user.as_ref() else { return; };\nlet Some(packages) = self.phone_packages.get(user.index) else { return; };","preventionTips":["Never index Vecs with data from Options; use .get() with fallback","Guard against device disconnect clearing selected_user while messages are queued","Test the app on startup and after device reconnect before any user selection","Centralize user selection so all filters read from one validated source"],"tags":["rust","panic","unwrap-none","index-out-of-bounds","iced"],"backgroundTag":"null-argument","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"}