GraphiteEditor/Graphite · error

Morph node does not exist

Error message

Morph node does not exist

What it means

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.

Source

Thrown at editor/src/messages/portfolio/document/graph_operation/utility_types.rs:112

		self.network_interface.insert_node(boolean_id, boolean, &[]);
		self.network_interface.move_node_to_chain_start(&boolean_id, layer, &[], self.import);
	}

	pub fn insert_blend_data(&mut self, layer: LayerNodeIdentifier, count: f64) -> NodeId {
		let blend = resolve_network_node_type("Blend")
			.expect("Blend node does not exist")
			.node_template_input_override([Some(NodeInput::type_default(list!(Graphic), true)), Some(NodeInput::value(TaggedValue::F64(count), false))]);

		let blend_id = NodeId::new();
		self.network_interface.insert_node(blend_id, blend, &[]);
		self.network_interface.move_node_to_chain_start(&blend_id, layer, &[], self.import);

		blend_id
	}

	pub fn insert_morph_data(&mut self, layer: LayerNodeIdentifier) -> NodeId {
		let morph = resolve_proto_node_type(graphene_std::vector::morph::IDENTIFIER)
			.expect("Morph node does not exist")
			.node_template_input_override([Some(NodeInput::type_default(list!(Graphic), true)), Some(NodeInput::value(TaggedValue::F64(0.5), false))]);

		let morph_id = NodeId::new();
		self.network_interface.insert_node(morph_id, morph, &[]);
		self.network_interface.move_node_to_chain_start(&morph_id, layer, &[], self.import);

		morph_id
	}

	/// Returns the Path node ID (the node closest to the layer's merge node in the chain).
	pub fn insert_control_path_data(&mut self, layer: LayerNodeIdentifier) -> NodeId {
		// Add Origins to Polyline node first (will be pushed deepest in the chain)
		let origins_to_polyline = resolve_network_node_type("Origins to Polyline")
			.expect("Origins to Polyline node does not exist")
			.default_node_template();
		let origins_to_polyline_id = NodeId::new();
		self.network_interface.insert_node(origins_to_polyline_id, origins_to_polyline, &[]);
		self.network_interface.move_node_to_chain_start(&origins_to_polyline_id, layer, &[], self.import);

View on GitHub (pinned to c507b35645)

Solutions

  1. Confirm the registry contains a definition whose identifier equals `graphene_std::vector::morph::IDENTIFIER`.
  2. Fix whichever side drifted: restore the IDENTIFIER value or update the registry entry.
  3. Handle `None`: log `"Morph proto node not registered"` and bail out before mutating the layer chain.
  4. Cover this identifier in a registry-completeness unit test.

Example fix

// before
let morph = resolve_proto_node_type(graphene_std::vector::morph::IDENTIFIER)
    .expect("Morph node does not exist")
    .node_template_input_override([...]);

// after
let Some(morph_def) = resolve_proto_node_type(graphene_std::vector::morph::IDENTIFIER) else {
    log::error!("Morph proto node not registered; skipping morph insert");
    return NodeId::new(); // or return Option<NodeId> and propagate
};
let morph = morph_def.node_template_input_override([...]);
Defensive patterns

Strategy: validation

Validate before calling

// Before the morph operation:
if document_node_definitions::resolve_proto_node_type(graphene_std::vector::morph::IDENTIFIER).is_none() {
    log::error!("Morph node not registered; command unavailable");
    return;
}

Type guard

fn morph_node_registered() -> bool {
    document_node_definitions::resolve_proto_node_type(graphene_std::vector::morph::IDENTIFIER).is_some()
}

Try / catch

// Rust has no try/catch; handle the Option:
let Some(morph_def) = resolve_proto_node_type(graphene_std::vector::morph::IDENTIFIER) else {
    log::error!("Morph node missing");
    return;
};

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of GraphiteEditor/Graphite@c507b35645 (2026-08-16). Data as JSON: /api/errors/e2e54c76638f2ede. Report an issue: GitHub.