{"record":{"id":"e2e54c76638f2ede","repo":"GraphiteEditor/Graphite","slug":"morph-node-does-not-exist","errorCode":null,"errorMessage":"Morph node does not exist","messagePattern":"Morph node does not exist","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"editor/src/messages/portfolio/document/graph_operation/utility_types.rs","lineNumber":112,"sourceCode":"\t\tself.network_interface.insert_node(boolean_id, boolean, &[]);\n\t\tself.network_interface.move_node_to_chain_start(&boolean_id, layer, &[], self.import);\n\t}\n\n\tpub fn insert_blend_data(&mut self, layer: LayerNodeIdentifier, count: f64) -> NodeId {\n\t\tlet blend = resolve_network_node_type(\"Blend\")\n\t\t\t.expect(\"Blend node does not exist\")\n\t\t\t.node_template_input_override([Some(NodeInput::type_default(list!(Graphic), true)), Some(NodeInput::value(TaggedValue::F64(count), false))]);\n\n\t\tlet blend_id = NodeId::new();\n\t\tself.network_interface.insert_node(blend_id, blend, &[]);\n\t\tself.network_interface.move_node_to_chain_start(&blend_id, layer, &[], self.import);\n\n\t\tblend_id\n\t}\n\n\tpub fn insert_morph_data(&mut self, layer: LayerNodeIdentifier) -> NodeId {\n\t\tlet morph = resolve_proto_node_type(graphene_std::vector::morph::IDENTIFIER)\n\t\t\t.expect(\"Morph node does not exist\")\n\t\t\t.node_template_input_override([Some(NodeInput::type_default(list!(Graphic), true)), Some(NodeInput::value(TaggedValue::F64(0.5), false))]);\n\n\t\tlet morph_id = NodeId::new();\n\t\tself.network_interface.insert_node(morph_id, morph, &[]);\n\t\tself.network_interface.move_node_to_chain_start(&morph_id, layer, &[], self.import);\n\n\t\tmorph_id\n\t}\n\n\t/// Returns the Path node ID (the node closest to the layer's merge node in the chain).\n\tpub fn insert_control_path_data(&mut self, layer: LayerNodeIdentifier) -> NodeId {\n\t\t// Add Origins to Polyline node first (will be pushed deepest in the chain)\n\t\tlet origins_to_polyline = resolve_network_node_type(\"Origins to Polyline\")\n\t\t\t.expect(\"Origins to Polyline node does not exist\")\n\t\t\t.default_node_template();\n\t\tlet origins_to_polyline_id = NodeId::new();\n\t\tself.network_interface.insert_node(origins_to_polyline_id, origins_to_polyline, &[]);\n\t\tself.network_interface.move_node_to_chain_start(&origins_to_polyline_id, layer, &[], self.import);","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/GraphiteEditor/Graphite/blob/c507b356453361e31638b8bff8f6d46b6da2961e/editor/src/messages/portfolio/document/graph_operation/utility_types.rs#L94-L130","documentation":"Panics when `resolve_proto_node_type(graphene_std::vector::morph::IDENTIFIER)` returns `None` in `insert_morph_data`. The Morph proto node (interpolating between two shapes at 0.5) is inserted at a layer's chain start; its definition must be registered in the static `DOCUMENT_NODE_TYPES` registry under the `morph` IDENTIFIER. The expect is a compile-time-style invariant against registry/identifier drift.","triggerScenarios":"Applying the Morph operation to layers (calls `insert_morph_data`, which hardcodes morph amount 0.5). Fires only if `graphene_std::vector::morph::IDENTIFIER` is absent from the registry — module moved, IDENTIFIER renamed, or definition entry removed/not added.","commonSituations":"Renaming the morph node or changing its `IDENTIFIER` constant without updating the registry; moving morph out of `graphene_std::vector`; new-node registration omissions when the registry list is maintained by hand.","solutions":["Confirm the registry contains a definition whose identifier equals `graphene_std::vector::morph::IDENTIFIER`.","Fix whichever side drifted: restore the IDENTIFIER value or update the registry entry.","Handle `None`: log `\"Morph proto node not registered\"` and bail out before mutating the layer chain.","Cover this identifier in a registry-completeness unit test."],"exampleFix":"// before\nlet morph = resolve_proto_node_type(graphene_std::vector::morph::IDENTIFIER)\n    .expect(\"Morph node does not exist\")\n    .node_template_input_override([...]);\n\n// after\nlet Some(morph_def) = resolve_proto_node_type(graphene_std::vector::morph::IDENTIFIER) else {\n    log::error!(\"Morph proto node not registered; skipping morph insert\");\n    return NodeId::new(); // or return Option<NodeId> and propagate\n};\nlet morph = morph_def.node_template_input_override([...]);","handlingStrategy":"validation","validationCode":"// Before the morph operation:\nif document_node_definitions::resolve_proto_node_type(graphene_std::vector::morph::IDENTIFIER).is_none() {\n    log::error!(\"Morph node not registered; command unavailable\");\n    return;\n}","typeGuard":"fn morph_node_registered() -> bool {\n    document_node_definitions::resolve_proto_node_type(graphene_std::vector::morph::IDENTIFIER).is_some()\n}","tryCatchPattern":"// Rust has no try/catch; handle the Option:\nlet Some(morph_def) = resolve_proto_node_type(graphene_std::vector::morph::IDENTIFIER) else {\n    log::error!(\"Morph node missing\");\n    return;\n};","preventionTips":["Keep IDENTIFIER constants as the single source of truth shared with registry tests","Cover morph in registry-change test matrices","Avoid expect in helpers that mutate the node graph","Register definitions atomically with node additions"],"tags":["rust","panic","expect","node-registry","proto-node","morph-node","graphite"],"backgroundTag":"node-definition-not-found","analyzedSha":"c507b356453361e31638b8bff8f6d46b6da2961e","analyzedAt":"2026-08-16T21:57:18.596Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}