{"record":{"id":"a3711b590429aa07","repo":"a-b-street/abstreet","slug":"invalid-tab-id","errorCode":null,"errorMessage":"invalid tab id: {}","messagePattern":"invalid tab id: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"widgetry/src/widgets/tabs.rs","lineNumber":81,"sourceCode":"            self.build_bar_items(ctx),\n            self.pop_active_content()\n                .container()\n                .tab_body(ctx)\n                .named(self.active_content_id()),\n        ])\n    }\n\n    pub fn handle_action(&mut self, ctx: &EventCtx, action: &str, panel: &mut Panel) -> bool {\n        if !action.starts_with(&self.id) {\n            return false;\n        }\n\n        let tab_idx = self\n            .tabs\n            .iter()\n            .enumerate()\n            .find(|(_idx, tab)| tab.tab_id == action)\n            .unwrap_or_else(|| panic!(\"invalid tab id: {}\", action))\n            .0;\n        self.activate_tab(ctx, tab_idx, panel);\n        true\n    }\n\n    pub fn active_tab_idx(&self) -> usize {\n        self.active_tab_idx\n    }\n\n    fn active_content_id(&self) -> String {\n        format!(\"{}_active_content\", self.id)\n    }\n\n    fn bar_items_id(&self) -> String {\n        format!(\"{}_bar_items\", self.id)\n    }\n\n    fn tab_id(&self, tab_index: usize) -> String {","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/a-b-street/abstreet/blob/0964f29315820c91b171b585eb51e300164e9197/widgetry/src/widgets/tabs.rs#L63-L99","documentation":"Tabs::handle_action is dispatched a TabAction carrying a tab id; it looks up the tab whose tab_id matches the action. If no tab has that id, it panics with \"invalid tab id\", because a tab action referencing a nonexistent tab is a programming bug rather than a recoverable condition.","triggerScenarios":"Sending a TabAction (click, close, etc.) for a tab id that is not present in the Tabs widget's tab list — e.g. the tab was already removed, the id came from stale state, or a mismatched id constant was used.","commonSituations":"Closing a tab twice; holding tab indices/ids across a rebuild of the panel; dynamic tabs whose ids change; typos in hardcoded tab id strings.","solutions":["Verify the tab id exists (Tabs::tabs list) before dispatching the action, or rebuild state after tab removal.","When handling close/middle-click events, check that the tab still exists and drop the action otherwise.","Regenerate or re-sync any cached tab ids after the panel is rebuilt."],"exampleFix":"// before\nctx.canvas.new_event(WidgetAction::Tabs(TabAction::SwitchTab(\"stale_id\".to_string())));\n// after\nif tabs.tabs.iter().any(|t| t.tab_id == \"stale_id\") {\n    ctx.canvas.new_event(WidgetAction::Tabs(TabAction::SwitchTab(\"stale_id\".to_string())));\n}","handlingStrategy":"validation","validationCode":"fn tab_exists(tabs: &Tabs, id: &str) -> bool { tabs.tabs.iter().any(|t| t.tab_id == id) }","typeGuard":null,"tryCatchPattern":"// Panic-based; guard before dispatch:\nif tab_exists(&tabs, &id) { ctx.canvas.new_event(WidgetAction::Tabs(TabAction::SwitchTab(id))); }","preventionTips":["Regenerate tab ids only from the Tabs widget itself, never from cached copies.","Clear stale tab state whenever the panel is rebuilt.","Ignore close actions for tabs that no longer exist."],"tags":["gui","panic","tabs","invalid-identifier"],"backgroundTag":"record-not-found","analyzedSha":"0964f29315820c91b171b585eb51e300164e9197","analyzedAt":"2026-09-13T18:02:03.421Z","contentChangedAt":"2026-09-13T18:02:03.421Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}