{"record":{"id":"8b74c44617eee934","repo":"GraphiteEditor/Graphite","slug":"no-state-for-selected-layer","errorCode":null,"errorMessage":"No state for selected layer","messagePattern":"No state for selected layer","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"editor/src/messages/tool/tool_messages/path_tool.rs","lineNumber":3082,"sourceCode":"\t\t\t\t\tif shape_editor.is_selected_layer(*layer) {\n\t\t\t\t\t\tif extend_selection && !tool_data.first_selected_with_single_click {\n\t\t\t\t\t\t\tresponses.add(NodeGraphMessage::SelectedNodesRemove { nodes: vec![layer.to_node()] });\n\n\t\t\t\t\t\t\tif let Some(selection) = &tool_data.stored_selection {\n\t\t\t\t\t\t\t\tlet mut selection = selection.clone();\n\t\t\t\t\t\t\t\tselection.remove(layer);\n\t\t\t\t\t\t\t\tshape_editor.selected_shape_state = selection;\n\t\t\t\t\t\t\t\ttool_data.stored_selection = None;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if shrink_selection && !tool_data.first_selected_with_single_click {\n\t\t\t\t\t\t\t// Only deselect all the points of the double clicked layer\n\t\t\t\t\t\t\tif let Some(selection) = &tool_data.stored_selection {\n\t\t\t\t\t\t\t\tlet selection = selection.clone();\n\t\t\t\t\t\t\t\tshape_editor.selected_shape_state = selection;\n\t\t\t\t\t\t\t\ttool_data.stored_selection = None;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tlet state = shape_editor.selected_shape_state.get_mut(layer).expect(\"No state for selected layer\");\n\t\t\t\t\t\t\tstate.deselect_all_points_in_layer();\n\t\t\t\t\t\t\tstate.deselect_all_segments_in_layer();\n\t\t\t\t\t\t} else if !tool_data.first_selected_with_single_click {\n\t\t\t\t\t\t\t// Select according to the selected editing mode\n\t\t\t\t\t\t\tlet point_editing_mode = tool_options.path_editing_mode.point_editing_mode;\n\t\t\t\t\t\t\tlet segment_editing_mode = tool_options.path_editing_mode.segment_editing_mode;\n\t\t\t\t\t\t\tshape_editor.select_connected(document, *layer, input.mouse.position, point_editing_mode, segment_editing_mode);\n\n\t\t\t\t\t\t\t// Select all the other layers back again\n\t\t\t\t\t\t\tif let Some(selection) = &tool_data.stored_selection {\n\t\t\t\t\t\t\t\tlet mut selection = selection.clone();\n\t\t\t\t\t\t\t\tselection.remove(layer);\n\n\t\t\t\t\t\t\t\tfor (layer, state) in selection {\n\t\t\t\t\t\t\t\t\tshape_editor.selected_shape_state.insert(layer, state);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\ttool_data.stored_selection = None;\n\t\t\t\t\t\t\t}","sourceCodeStart":3064,"sourceCodeEnd":3100,"githubUrl":"https://github.com/GraphiteEditor/Graphite/blob/c507b356453361e31638b8bff8f6d46b6da2961e/editor/src/messages/tool/tool_messages/path_tool.rs#L3064-L3100","documentation":"During a double-click on a layer in the path tool with shrink_selection, the code restores the stored multi-selection and then fetches that layer's per-layer editing state with selected_shape_state.get_mut(layer).expect(\"No state for selected layer\"). The map only contains layers that were previously selected as editable shapes, so the expect fires when the double-clicked layer has no entry — the shape editor's selection state and the layer being clicked have desynced (e.g., the layer was never added via select_connected, or the restored stored_selection lacks it). This is a state-machine desync inside the tool, not a registry issue.","triggerScenarios":"Double-clicking a path layer while shrink_selection is true and first_selected_with_single_click is false: the branch restores tool_data.stored_selection into shape_editor.selected_shape_state and immediately calls get_mut(layer); if that layer was not part of the shape selection state (first click selected a different layer, or selection was cleared), the expect panics.","commonSituations":"Rapid double-clicks across different layers where the second click lands on a layer never added by select_connected; undo between clicks resetting shape state but not stored_selection; layers whose shape state was removed by a selection-removal branch just above this code (the same function removes the layer from the map in the first branch); stale stored_selection after document mutations delete and recreate layers.","solutions":["Replace expect with if let Some(state) = shape_editor.selected_shape_state.get_mut(layer) so a missing entry simply skips deselection.","Before the shrink branch, ensure the clicked layer has shape state: if !selected_shape_state.contains_key(layer), initialize it via select_connected first.","Clear tool_data.stored_selection whenever the shape editor's selected_shape_state changes structure (deletions, undo) so a restore never injects a stale map.","Write a unit/integration test that double-clicks an unselected layer with shrink_selection to lock the guarded behavior."],"exampleFix":"// before\nlet state = shape_editor.selected_shape_state.get_mut(layer).expect(\"No state for selected layer\");\nstate.deselect_all_points_in_layer();\nstate.deselect_all_segments_in_layer();\n\n// after\nif let Some(state) = shape_editor.selected_shape_state.get_mut(layer) {\n\tstate.deselect_all_points_in_layer();\n\tstate.deselect_all_segments_in_layer();\n} else {\n\tlog::warn!(\"path tool: no shape state for layer {layer:?} during shrink selection\");\n}","handlingStrategy":"validation","validationCode":"if shape_editor.selected_shape_state.contains_key(layer) {\n\tlet state = shape_editor.selected_shape_state.get_mut(layer).unwrap();\n\tstate.deselect_all_points_in_layer();\n\tstate.deselect_all_segments_in_layer();\n} else {\n\t// layer never selected as editable shape: nothing to shrink\n}","typeGuard":"fn has_shape_state(shape_editor: &ShapeState, layer: LayerNodeIdentifier) -> bool {\n\tshape_editor.selected_shape_state.contains_key(&layer)\n}","tryCatchPattern":null,"preventionTips":["Never expect on map entries keyed by user-interaction-derived ids (clicked layers); use if-let.","Keep stored_selection and selected_shape_state lifecycle in sync: clear both together on undo, deletion, and document switches.","Add double-click interaction tests covering layers that were not previously selected."],"tags":["rust","graphite-editor","path-tool","missing-map-entry","state-desync","expect-panic","selection"],"backgroundTag":"missing-map-entry","analyzedSha":"c507b356453361e31638b8bff8f6d46b6da2961e","analyzedAt":"2026-08-16T21:57:18.596Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}