GraphiteEditor/Graphite · error · syn::Error

Validation error: {e}

Error message

Validation error:
{e}

What it means

Emitted when validate_node_fn rejects a node function that parsed successfully — semantic rules rather than syntax. The validator set covers implementations for generics, primary-input exposure, min/max usage, slider bounds, Item<T> parameter pairing, element-wise structure, and ranked inputs. Most current validators report via proc-macro-error's emit_error! with their own messages, so this wrapper text is the hard-failure path of validate_node_fn.

Source

Thrown at node-graph/node-macro/src/parsing.rs:1029

}

fn parse_output(output: &ReturnType) -> syn::Result<Type> {
	match output {
		ReturnType::Default => Ok(syn::parse_quote!(())),
		ReturnType::Type(_, ty) => Ok((**ty).clone()),
	}
}

fn extract_attribute<'a>(attrs: &'a [Attribute], name: &str) -> Option<&'a Attribute> {
	attrs.iter().find(|attr| attr.path().is_ident(name))
}

// Modify the new_node_fn function to use the code generation
pub fn new_node_fn(attr: TokenStream2, item: TokenStream2) -> syn::Result<TokenStream2> {
	let crate_ident = CrateIdent::default();
	let mut parsed_node = parse_node_fn(attr, item.clone()).map_err(|e| Error::new(e.span(), format!("Failed to parse node function:\n{e}")))?;
	parsed_node.replace_impl_trait_in_input();
	crate::validation::validate_node_fn(&parsed_node).map_err(|e| Error::new(e.span(), format!("Validation error:\n{e}")))?;
	generate_node_code(&crate_ident, &parsed_node).map_err(|e| Error::new(e.span(), format!("Failed to generate node code:\n{e}")))
}

impl ParsedNodeFn {
	/// The node's primary: the first argument (non-environment) field, whose declared shape classifies the node
	/// as an element-wise kernel, aggregation, or generator. Returns the field with its index in `fields`.
	pub(crate) fn primary_input_field(&self) -> Option<(usize, &ParsedField)> {
		self.fields.iter().enumerate().find(|(_, field)| !field.is_environment())
	}

	pub fn replace_impl_trait_in_input(&mut self) {
		if let Type::ImplTrait(impl_trait) = self.input.ty.clone() {
			let ident = Ident::new("_Input", impl_trait.span());
			let mut bounds = impl_trait.bounds;
			bounds.push(parse_quote!('n));
			self.fn_generics.push(GenericParam::Type(TypeParam {
				attrs: Default::default(),
				ident: ident.clone(),

View on GitHub (pinned to c507b35645)

Solutions

  1. Read the validator text after the header — it names the rule and the offending parameter
  2. Give Item<T> parameters a ranked primary input (Item<T>, List<T>, or ListDyn), or drop the Item wrapper
  3. Add #[implementations(...)] for every generic parameter, or opt out with skip_impl when implementations are registered manually
  4. Check min/max and slider-bound attributes against their constraints

Example fix

// before: Item<T> parameter but an unranked primary input
#[node_macro::node(category("Color"))]
fn blend(base: f64, #[implementations(f32, f64)] each: Item<f64>) -> f64 { /* ... */ }

// after: make the primary input ranked so the frame matches
#[node_macro::node(category("Color"))]
fn blend(base: List<f64>, #[implementations(f32, f64)] each: Item<f64>) -> f64 { /* ... */ }
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: A parsed node whose shape violates the rules: an Item<T> parameter on a node whose primary input is not ranked (Item<T>, List<T>, or ListDyn); #[implementations(...)] on a ranked parameter containing non-bare element types; generic type parameters without implementations when skip_impl is not set; invalid min/max or slider-bound attribute combinations.

Common situations: Adding a per-element (Item<T>) parameter to a node whose primary input is a plain value; forgetting #[implementations(...)] on a newly introduced generic parameter; converting an aggregation node to a generator while leaving ranked parameters behind.

Related errors


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