GraphiteEditor/Graphite · critical

Failed to create transform node

Error message

Failed to create transform node

What it means

Same registry mechanism as the Spline case: resolve_proto_node_type() (document_node_definitions.rs:1480) looks up graphene_std::transform_nodes::transform::IDENTIFIER in the static DOCUMENT_NODE_TYPES map and returns None if the definition is absent. This .expect() (graph_modification_utils.rs:149-151) runs unconditionally at the end of the layer-merge helper to insert the Transform node that anchors future tool modifications, so a missing registration panics every merge, not just spline merges.

Source

Thrown at editor/src/messages/tool/common_functionality/graph_modification_utils.rs:150

	if current_and_other_layer_is_spline {
		let spline_node_id = NodeId::new();
		let spline_node = document_node_definitions::resolve_proto_node_type(graphene_std::vector::spline::IDENTIFIER)
			.expect("Failed to create Spline node")
			.default_node_template();
		responses.add(NodeGraphMessage::InsertNode {
			node_id: spline_node_id,
			node_template: Box::new(spline_node),
		});
		responses.add(NodeGraphMessage::MoveNodeToChainStart {
			node_id: spline_node_id,
			parent: first_layer,
		});
	}

	// Add a transform node to ensure correct tooling modifications
	let transform_node_id = NodeId::new();
	let transform_node = document_node_definitions::resolve_proto_node_type(graphene_std::transform_nodes::transform::IDENTIFIER)
		.expect("Failed to create transform node")
		.default_node_template();
	responses.add(NodeGraphMessage::InsertNode {
		node_id: transform_node_id,
		node_template: Box::new(transform_node),
	});
	responses.add(NodeGraphMessage::MoveNodeToChainStart {
		node_id: transform_node_id,
		parent: first_layer,
	});

	responses.add(NodeGraphMessage::RunDocumentGraph);
	responses.add(DeferMessage::AfterGraphRun {
		messages: vec![PenToolMessage::RecalculateLatestPointsPosition.into()],
	});
}

/// Merge the `first_endpoint` with `second_endpoint`.
pub fn merge_points(document: &DocumentMessageHandler, layer: LayerNodeIdentifier, first_endpoint: PointId, second_endpont: PointId, responses: &mut VecDeque<Message>) {

View on GitHub (pinned to c507b35645)

Solutions

  1. Verify resolve_proto_node_type(graphene_std::transform_nodes::transform::IDENTIFIER) is Some by checking the registration entry in document_node_definitions.rs matches the const exactly
  2. Restore or rename the registration so the identifier and the const agree, then rebuild
  3. Convert the expect to let-else with an error log so the merge can proceed without the transform node (or abort cleanly) instead of unwinding
  4. Cover the whole merge chain (Combine Paths, Path, Spline, Transform) in one registry-drift unit test so a single edit cannot break them silently

Example fix

// before
let transform_node = document_node_definitions::resolve_proto_node_type(graphene_std::transform_nodes::transform::IDENTIFIER)
	.expect("Failed to create transform node")
	.default_node_template();
// after
let Some(transform_node) = document_node_definitions::resolve_proto_node_type(graphene_std::transform_nodes::transform::IDENTIFIER) else {
	log::error!("Transform node missing from DOCUMENT_NODE_TYPES; aborting merge");
	return;
};
let transform_node = transform_node.default_node_template();
Defensive patterns

Strategy: validation

Validate before calling

#[test]
fn merge_chain_nodes_registered() {
	for id in [graphene_std::vector::combine_paths::IDENTIFIER, graphene_std::vector::spline::IDENTIFIER, graphene_std::transform_nodes::transform::IDENTIFIER] {
		assert!(document_node_definitions::resolve_proto_node_type(id).is_some(), "DOCUMENT_NODE_TYPES missing entry for {id:?}");
	}
}

Prevention

When it happens

Trigger: Any layer-merge operation (tool flow reaching new_merged_layer_flow) when the Transform proto node identifier has no DOCUMENT_NODE_TYPES entry — renamed const, deleted or relocated transform implementation, or an edited registration list.

Common situations: Renaming the transform node during a graph refactor while tool code keeps the old IDENTIFIER; upgrading between Graphite revisions where Transform changed namespace (graphene_std::transform_nodes); building with feature flags that exclude the transform module.

Related errors


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