{"record":{"id":"641fb4f6759c6c53","repo":"GraphiteEditor/Graphite","slug":"color-value-node-does-not-exist","errorCode":null,"errorMessage":"Color Value node does not exist","messagePattern":"Color Value node does not exist","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"editor/src/messages/portfolio/document/graph_operation/utility_types.rs","lineNumber":239,"sourceCode":"\n\t\tself.responses.add(DocumentMessage::Resource(ResourceMessage::AddFont { resource_id: font_resource_id, font }));\n\n\t\tlet text_to_vector_id = NodeId::new();\n\t\tself.network_interface.insert_node(text_to_vector_id, text_to_vector, &[]);\n\t\tself.network_interface.move_node_to_chain_start(&text_to_vector_id, layer, &[], self.import);\n\n\t\tlet transform_id = NodeId::new();\n\t\tself.network_interface.insert_node(transform_id, transform, &[]);\n\t\tself.network_interface.move_node_to_chain_start(&transform_id, layer, &[], self.import);\n\n\t\tlet fill_id = NodeId::new();\n\t\tself.network_interface.insert_node(fill_id, fill, &[]);\n\t\tself.network_interface.move_node_to_chain_start(&fill_id, layer, &[], self.import);\n\t}\n\n\tpub fn insert_color_value(&mut self, color: Color, layer: LayerNodeIdentifier, attachment_input: InputConnector) -> NodeId {\n\t\tlet color_value = resolve_proto_node_type(graphene_std::math_nodes::color_value::IDENTIFIER)\n\t\t\t.expect(\"Color Value node does not exist\")\n\t\t\t.node_template_input_override([Some(NodeInput::value(TaggedValue::None, false)), Some(NodeInput::value(TaggedValue::Color(color), false))]);\n\n\t\tlet color_value_id = NodeId::new();\n\t\tself.network_interface.insert_node(color_value_id, color_value, &[]);\n\t\tself.start_paint_chain(&color_value_id, layer, attachment_input);\n\n\t\tcolor_value_id\n\t}\n\n\t/// Clear the whole-expanse paint one tool left on a layer so the other can start its own chain there.\n\t/// Severing at the attachment detaches the layer from whatever the walk stopped at, which is the only part a node\n\t/// the rest of the graph also draws from is subjected to, since such a node is never among those deleted.\n\tfn clear_paint_chain(&mut self, paint_chain: &ReplaceablePaintChain) {\n\t\tself.network_interface.disconnect_input(&paint_chain.attachment_input, &[]);\n\n\t\tif !paint_chain.nodes.is_empty() {\n\t\t\tself.network_interface.delete_nodes(paint_chain.nodes.clone(), false, &[]);\n\t\t}","sourceCodeStart":221,"sourceCodeEnd":257,"githubUrl":"https://github.com/GraphiteEditor/Graphite/blob/c507b356453361e31638b8bff8f6d46b6da2961e/editor/src/messages/portfolio/document/graph_operation/utility_types.rs#L221-L257","documentation":"Panics when `resolve_proto_node_type(graphene_std::math_nodes::color_value::IDENTIFIER)` returns `None` in `insert_color_value`. Paint tools attach a Color Value node (fed into a layer's paint attachment input) to produce solid colors; the proto-node definition must be registered in the static `DOCUMENT_NODE_TYPES` registry. The expect is the standard registry-drift tripwire for the `color_value` identifier.","triggerScenarios":"A tool applying a solid fill/stroke color to a layer via `insert_color_value` (then `start_paint_chain` wires it to the attachment input). Fires when the `math_nodes::color_value` IDENTIFIER is missing from the registry — node renamed, moved out of `math_nodes`, or definition never added.","commonSituations":"Refactors of `graphene_std::math_nodes`; renaming the Color Value node or its identifier; adding math nodes without registering their editor-side definitions.","solutions":["Verify the registry contains a definition matching `graphene_std::math_nodes::color_value::IDENTIFIER`.","Repair the drift: restore the IDENTIFIER value or re-add the registry entry.","Handle `None` with a logged early return so the paint operation is skipped rather than crashing mid-stroke.","Cover this identifier in registry-completeness tests."],"exampleFix":"// before\nlet color_value = resolve_proto_node_type(graphene_std::math_nodes::color_value::IDENTIFIER)\n    .expect(\"Color Value node does not exist\")\n    .node_template_input_override([...]);\n\n// after\nlet Some(cv_def) = resolve_proto_node_type(graphene_std::math_nodes::color_value::IDENTIFIER) else {\n    log::error!(\"Color Value proto node not registered; skipping paint insert\");\n    return NodeId::new();\n};\nlet color_value = cv_def.node_template_input_override([...]);","handlingStrategy":"validation","validationCode":"// Before a paint tool applies a color:\nif document_node_definitions::resolve_proto_node_type(graphene_std::math_nodes::color_value::IDENTIFIER).is_none() {\n    log::error!(\"Color Value node not registered; paint disabled\");\n    return;\n}","typeGuard":"fn color_value_registered() -> bool {\n    document_node_definitions::resolve_proto_node_type(graphene_std::math_nodes::color_value::IDENTIFIER).is_some()\n}","tryCatchPattern":"// Rust has no try/catch; branch on the Option before starting the paint chain:\nlet Some(cv_def) = resolve_proto_node_type(graphene_std::math_nodes::color_value::IDENTIFIER) else {\n    log::error!(\"Color Value missing; skipping paint\");\n    return;\n};","preventionTips":["Include math_nodes identifiers in registry-completeness tests","Guard paint flows so a missing node skips painting instead of crashing mid-stroke","Synchronize math_nodes renames with the registry","Use let-else over expect in paint helpers"],"tags":["rust","panic","expect","node-registry","proto-node","color-value","paint","graphite"],"backgroundTag":"node-definition-not-found","analyzedSha":"c507b356453361e31638b8bff8f6d46b6da2961e","analyzedAt":"2026-08-16T21:57:18.596Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}