{"record":{"id":"1ae71c10d40d3ef9","repo":"GraphiteEditor/Graphite","slug":"solidify-stroke-node-should-exist","errorCode":null,"errorMessage":"Solidify Stroke node should exist","messagePattern":"Solidify Stroke node should exist","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"editor/src/messages/portfolio/document/document_message_handler.rs","lineNumber":2746,"sourceCode":"\n\t\t\t\tnew_folders.push(DocumentMessageHandler::group_layers(responses, insert_index, parent, group_folder_type, &mut self.network_interface));\n\t\t\t}\n\n\t\t\tresponses.add(NodeGraphMessage::SelectedNodesSet { nodes: new_folders });\n\t\t}\n\t}\n\n\t/// For each selected layer, splits its fill and stroke into two stacked layers connected\n\t/// to a shared `Solidify Stroke` node via two `Item at Index` nodes (indices 0 and 1).\n\t/// Layers with only a stroke get just a `Solidify Stroke` added.\n\t/// Layers with only a fill, or neither, are left untouched.\n\tfn handle_expand_fill_stroke_on_selected_layers(&mut self, responses: &mut VecDeque<Message>) {\n\t\tlet selected_layers: Vec<LayerNodeIdentifier> = self.network_interface.selected_nodes().selected_layers(self.metadata()).collect();\n\t\tif selected_layers.is_empty() {\n\t\t\treturn;\n\t\t}\n\n\t\tlet solidify_stroke_definition = document_node_definitions::resolve_proto_node_type(graphene_std::vector::solidify_stroke::IDENTIFIER).expect(\"Solidify Stroke node should exist\");\n\t\tlet item_at_index_definition = document_node_definitions::resolve_proto_node_type(graphene_std::graphic::item_at_index::IDENTIFIER).expect(\"Item at Index node should exist\");\n\n\t\tlet mut resulting_layers: Vec<NodeId> = Vec::new();\n\n\t\tfor layer in selected_layers {\n\t\t\tif !self.network_interface.document_metadata().layer_vector_data.contains_key(&layer) {\n\t\t\t\tresulting_layers.push(layer.to_node());\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet appearance = self.network_interface.document_metadata().layer_appearance_attributes.get(&layer);\n\n\t\t\tlet has_fill = appearance.is_some_and(|appearance| appearance.has_painted_cover(Cover::Fill));\n\t\t\t// A visible stroke needs both renderable geometry (non-zero weight) and paint that draws something\n\t\t\tlet has_stroke = appearance.is_some_and(|appearance| {\n\t\t\t\tappearance.first_coverage_of(Cover::Stroke).is_some_and(|coverage| coverage.stroke_params().has_renderable_stroke())\n\t\t\t\t\t&& appearance.first_paint_of(Cover::Stroke).is_some_and(|paint| !paint.is_guaranteed_fully_transparent())\n\t\t\t});","sourceCodeStart":2728,"sourceCodeEnd":2764,"githubUrl":"https://github.com/GraphiteEditor/Graphite/blob/c507b356453361e31638b8bff8f6d46b6da2961e/editor/src/messages/portfolio/document/document_message_handler.rs#L2728-L2764","documentation":"Panics when `document_node_definitions::resolve_proto_node_type(graphene_std::vector::solidify_stroke::IDENTIFIER)` returns `None`. The function looks the node up in the static `DOCUMENT_NODE_TYPES` registry (document_node_definitions.rs:1480-1486); the Graphite editor assumes every proto node its handlers reference is registered there at build time. The expect is a static-invariant assertion: it can only fire if the registry and the `solidify_stroke` IDENTIFIER constant have drifted apart (rename, move, or missing registration).","triggerScenarios":"Invoked from `handle_expand_fill_stroke_on_selected_layers`, i.e. the user runs the Expand Fill/Stroke command on one or more selected layers. The panic fires only when the `Solidify Stroke` proto node definition is absent from `DOCUMENT_NODE_TYPES` — e.g. the node was renamed in graphene_std, moved modules, its `IDENTIFIER` constant changed, or it was never added to the node registry list.","commonSituations":"Renaming a proto node struct or its `IDENTIFIER` without updating `document_node_definitions.rs`; moving `solidify_stroke` between crates/modules; adding a new node to graphene_std but forgetting the registry entry; builds where a feature flag excludes the node definition module.","solutions":["Confirm the registry entry exists: search `DOCUMENT_NODE_TYPES` / the generate-registry macro in `editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs` for the Solidify Stroke definition.","If the node was renamed or moved, restore the old `IDENTIFIER` value in `graphene_std::vector::solidify_stroke` or update the call site to the new identifier.","If this is a new node, add its `DocumentNodeDefinition` to the registry (correct `ProtoNodeIdentifier`, category, and template).","Replace the `expect` with graceful `Option` handling (log and return) so a registry drift degrades instead of crashing the editor."],"exampleFix":"// before\nlet solidify_stroke_definition = document_node_definitions::resolve_proto_node_type(graphene_std::vector::solidify_stroke::IDENTIFIER).expect(\"Solidify Stroke node should exist\");\n\n// after\nlet Some(solidify_stroke_definition) = document_node_definitions::resolve_proto_node_type(graphene_std::vector::solidify_stroke::IDENTIFIER) else {\n    log::error!(\"Solidify Stroke node definition is not registered; skipping expand fill/stroke\");\n    return;\n};","handlingStrategy":"validation","validationCode":"// Before running the expand fill/stroke command, confirm the definitions exist:\nlet ok = [\n    graphene_std::vector::solidify_stroke::IDENTIFIER,\n    graphene_std::graphic::item_at_index::IDENTIFIER,\n].iter().all(|id| document_node_definitions::resolve_proto_node_type(*id).is_some());\nif !ok {\n    log::error!(\"Expand Fill/Stroke unavailable: required node definitions not registered\");\n    return;\n}","typeGuard":"fn solidify_stroke_registered() -> bool {\n    document_node_definitions::resolve_proto_node_type(graphene_std::vector::solidify_stroke::IDENTIFIER).is_some()\n}","tryCatchPattern":"// Rust has no try/catch; isolate the panic at a boundary if unavoidable:\nlet template = std::panic::catch_unwind(|| {\n    document_node_definitions::resolve_proto_node_type(graphene_std::vector::solidify_stroke::IDENTIFIER)\n        .map(|d| d.default_node_template())\n});\nif template.is_err() {\n    log::error!(\"solidify stroke lookup panicked; registry is inconsistent\");\n}","preventionTips":["Add a startup/CI test that resolves every IDENTIFIER referenced by message handlers","Never rename a proto node IDENTIFIER without grepping editor/src for its use sites","Treat resolve_proto_node_type as fallible: log-and-return instead of expect in message handlers","When adding a node, register its definition in the same commit that introduces the IDENTIFIER constant"],"tags":["rust","panic","expect","node-registry","proto-node","graphite","solidify-stroke"],"backgroundTag":"node-definition-not-found","analyzedSha":"c507b356453361e31638b8bff8f6d46b6da2961e","analyzedAt":"2026-08-16T21:57:18.596Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}