GraphiteEditor/Graphite · error

Auto-Tangents node does not exist

Error message

Auto-Tangents node does not exist

What it means

Panics when `resolve_proto_node_type(graphene_std::vector::auto_tangents::IDENTIFIER)` returns `None` in `insert_control_path_data`, which inserts an Auto-Tangents node (spread=1, preserve_existing=false) between Origins to Polyline and Path. The proto-node definition must be present in the static `DOCUMENT_NODE_TYPES` registry; the expect guards the registry/IDENTIFIER invariant.

Source

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

		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);

		// Add Auto-Tangents node (between Origins to Polyline and Path), with spread=1 and preserve_existing=false
		let auto_tangents = resolve_proto_node_type(graphene_std::vector::auto_tangents::IDENTIFIER)
			.expect("Auto-Tangents node does not exist")
			.node_template_input_override([None, Some(NodeInput::value(TaggedValue::F64(1.), false)), Some(NodeInput::value(TaggedValue::Bool(false), false))]);
		let auto_tangents_id = NodeId::new();
		self.network_interface.insert_node(auto_tangents_id, auto_tangents, &[]);
		self.network_interface.move_node_to_chain_start(&auto_tangents_id, layer, &[], self.import);

		// Add Path node to chain start (closest to the Merge node)
		let path = resolve_network_node_type("Path").expect("Path node does not exist").default_node_template();
		let path_id = NodeId::new();
		self.network_interface.insert_node(path_id, path, &[]);
		self.network_interface.move_node_to_chain_start(&path_id, layer, &[], self.import);

		path_id
	}

	pub fn insert_vector(&mut self, subpaths: Vec<Subpath<PointId>>, layer: LayerNodeIdentifier, include_transform: bool, include_fill: bool, include_stroke: bool) {
		// Build a VectorModification that reproduces the geometry (same format the Pen tool uses)
		let vector = Vector::from_subpaths(subpaths, true);
		let modification = Box::new(VectorModification::create_from_vector(&vector));

View on GitHub (pinned to c507b35645)

Solutions

  1. Verify a registry entry exists whose identifier equals `graphene_std::vector::auto_tangents::IDENTIFIER`.
  2. Repair the drift: restore the IDENTIFIER or re-add the definition entry.
  3. Handle `None` with a logged early return so the chain insert aborts cleanly instead of panicking.
  4. Include this identifier in an automated registry-completeness check.

Example fix

// before
let auto_tangents = resolve_proto_node_type(graphene_std::vector::auto_tangents::IDENTIFIER)
    .expect("Auto-Tangents node does not exist")
    .node_template_input_override([...]);

// after
let Some(at_def) = resolve_proto_node_type(graphene_std::vector::auto_tangents::IDENTIFIER) else {
    log::error!("Auto-Tangents proto node not registered; aborting control path insert");
    return NodeId::new();
};
let auto_tangents = at_def.node_template_input_override([...]);
Defensive patterns

Strategy: validation

Validate before calling

// Before building a control path:
if document_node_definitions::resolve_proto_node_type(graphene_std::vector::auto_tangents::IDENTIFIER).is_none() {
    log::error!("Auto-Tangents node not registered; control path unavailable");
    return;
}

Type guard

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

Try / catch

// Rust has no try/catch; handle the Option:
let Some(at_def) = resolve_proto_node_type(graphene_std::vector::auto_tangents::IDENTIFIER) else {
    log::error!("Auto-Tangents missing");
    return;
};

Prevention

When it happens

Trigger: Creating a control path for a layer via `insert_control_path_data`. Fires when the `auto_tangents` IDENTIFIER resolves to no registry entry — the node moved out of `graphene_std::vector`, its IDENTIFIER changed, or the definition was never registered.

Common situations: Module reorganizations in graphene_std; renaming the Auto-Tangents node or its identifier constant; hand-maintained registry lists missing a newly added node.

Related errors


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