{"record":{"id":"e0a80f6f386a0c21","repo":"GraphiteEditor/Graphite","slug":"the-active-tool-is-not-initialized","errorCode":null,"errorMessage":"The active tool is not initialized","messagePattern":"The active tool is not initialized","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"editor/src/messages/tool/utility_types.rs","lineNumber":237,"sourceCode":"\t}\n\tfn tool_type(&self) -> ToolType;\n}\n\npub struct ToolData {\n\tpub active_tool_type: ToolType,\n\tpub active_shape_type: Option<ToolType>,\n\tpub tools: HashMap<ToolType, Box<Tool>>,\n}\n\nimpl fmt::Debug for ToolData {\n\tfn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n\t\tf.debug_struct(\"ToolData\").field(\"active_tool_type\", &self.active_tool_type).field(\"tool_options\", &\"[…]\").finish()\n\t}\n}\n\nimpl ToolData {\n\tpub fn active_tool_mut(&mut self) -> &mut Box<Tool> {\n\t\tself.tools.get_mut(&self.active_tool_type).expect(\"The active tool is not initialized\")\n\t}\n\n\tpub fn active_tool(&self) -> &Tool {\n\t\tself.tools.get(&self.active_tool_type).map(|x| x.as_ref()).expect(\"The active tool is not initialized\")\n\t}\n}\n\nimpl ToolData {\n\tpub fn send_layout(&self, responses: &mut VecDeque<Message>, layout_target: LayoutTarget, brush_tool: bool) {\n\t\tresponses.add(LayoutMessage::SendLayout {\n\t\t\tlayout: self.layout(brush_tool),\n\t\t\tlayout_target,\n\t\t});\n\t}\n\n\tfn layout(&self, brush_tool: bool) -> Layout {\n\t\tlet active_tool = self.active_shape_type.unwrap_or(self.active_tool_type);\n","sourceCodeStart":219,"sourceCodeEnd":255,"githubUrl":"https://github.com/GraphiteEditor/Graphite/blob/c507b356453361e31638b8bff8f6d46b6da2961e/editor/src/messages/tool/utility_types.rs#L219-L255","documentation":"ToolData::active_tool_mut fetches the currently active tool from the tools HashMap keyed by ToolType, panicking if active_tool_type names a tool that was never inserted. ToolData is the editor's tool dispatcher, so every mutable tool interaction (mouse/keyboard events sent to the active tool) funnels through this method. The expect therefore asserts a construction invariant: every ToolType value that can become active must have a registered Tool in the map before any event dispatch. It is violated when a new ToolType variant is added without registering its tool, or when active_tool_type is set from persisted/external input that bypasses registration.","triggerScenarios":"Any message-dispatch path that calls tool_data.active_tool_mut() — e.g., pointer/keyboard events routed to the active tool — after active_tool_type was set to a ToolType for which tools.get_mut returns None (unregistered variant or tool map not yet populated at startup).","commonSituations":"Adding a ToolType enum variant and forgetting the matching insert in ToolData construction; reordering initialization so active_tool_type defaults before the tools map is filled; restoring a persisted active tool from preferences that this build no longer registers; tests constructing ToolData manually without all tools.","solutions":["Ensure the tool registration covers every ToolType variant: assert tools.len() == number of variants (or use a match that forces exhaustive registration) when building ToolData.","On startup, validate that tools.contains_key(&active_tool_type) before entering the message loop and fall back to a known-registered default (e.g., Select).","When adding a new tool, register it in the same commit as the enum variant; run the editor and cycle through all tools once in CI smoke tests.","If preferences can restore an active tool, sanitize the stored value against the registered set before assigning."],"exampleFix":"// before\npub fn active_tool_mut(&mut self) -> &mut Box<Tool> {\n\tself.tools.get_mut(&self.active_tool_type).expect(\"The active tool is not initialized\")\n}\n\n// after\npub fn active_tool_mut(&mut self) -> &mut Box<Tool> {\n\tself.tools.get_mut(&self.active_tool_type).unwrap_or_else(|| panic!(\"ToolType {:?} has no registered tool; register it in ToolData::new\", self.active_tool_type))\n}","handlingStrategy":"validation","validationCode":"// Before entering the message loop / dispatching to tools\nif !tool_data.tools.contains_key(&tool_data.active_tool_type) {\n\ttool_data.active_tool_type = ToolType::Select; // known-registered fallback\n}\nlet tool = tool_data.active_tool_mut();","typeGuard":"fn active_tool_ready(tool_data: &ToolData) -> bool {\n\ttool_data.tools.contains_key(&tool_data.active_tool_type)\n}","tryCatchPattern":null,"preventionTips":["Register every ToolType variant in ToolData's constructor; use an exhaustive match on the enum so additions are compiler-flagged.","Validate (and sanitize) restored active-tool state from preferences against the registered set.","Add a startup assertion that the active tool resolves before any event dispatch."],"tags":["rust","graphite-editor","tool-dispatch","uninitialized-state","expect-panic","hashmap-lookup"],"backgroundTag":"uninitialized-state-access","analyzedSha":"c507b356453361e31638b8bff8f6d46b6da2961e","analyzedAt":"2026-08-16T21:57:18.596Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}