GraphiteEditor/Graphite · error · syn::Error

Failed to parse node_fn attributes: {e}

Error message

Failed to parse node_fn attributes:
{e}

What it means

Top-level wrapper for failures while parsing the #[node_macro::node(...)] attribute arguments (NodeFnAttributes). Every specific attribute problem — unsupported key, wrong literal shape, duplicate key, or the missing required category — bubbles up through this message with the inner error appended on the next line.

Source

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

		}

		Ok(NodeFnAttributes {
			category,
			display_name,
			path,
			skip_impl,
			properties_string,
			cfg,
			shader_node,
			serialize,
			memoize,
			inject_scope,
		})
	}
}

pub(crate) fn parse_node_fn(attr: TokenStream2, item: TokenStream2) -> syn::Result<ParsedNodeFn> {
	let attributes = syn::parse2::<NodeFnAttributes>(attr.clone()).map_err(|e| Error::new(e.span(), format!("Failed to parse node_fn attributes:\n{e}")))?;
	let input_fn = syn::parse2::<ItemFn>(item.clone()).map_err(|e| Error::new(e.span(), format!("Failed to parse function: {e}. Make sure it's a valid Rust function.")))?;

	let vis = input_fn.vis;
	let fn_name = input_fn.sig.ident.clone();
	let struct_name = format_ident!("{}", fn_name.to_string().to_case(Case::Pascal));
	let mod_name = fn_name.clone();
	let fn_generics = input_fn.sig.generics.params.into_iter().collect();
	let is_async = input_fn.sig.asyncness.is_some();

	let (input, fields) = parse_inputs(&input_fn.sig.inputs)?;
	let output_type = parse_output(&input_fn.sig.output)?;
	let output_element = peel_item(&output_type);
	let where_clause = input_fn.sig.generics.where_clause;
	let body = input_fn.block.to_token_stream();
	let description = input_fn
		.attrs
		.iter()
		.filter_map(|a| {

View on GitHub (pinned to c507b35645)

Solutions

  1. Read the appended inner error — it names the real problem (e.g. "Unsupported attribute in `node`" or "The attribute 'category' is required")
  2. Restrict keys to the supported list with documented shapes: category("Value"), name("X"), path(core::T), properties("fn_name"), shader_node(T), serialize(path), cfg(...), and bare flags skip_impl, memoize, inject_scope
  3. Always include category(...) — category("") is valid when a node should be hidden from the catalog

Example fix

// before: missing the required category(...)
#[node_macro::node(name("My Node"))]
fn my_node(...) -> ... { /* ... */ }

// after
#[node_macro::node(category("Value"), name("My Node"))]
fn my_node(...) -> ... { /* ... */ }
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: An unknown attribute key (anything other than category, name, path, skip_impl, properties, cfg, shader_node, serialize, memoize, inject_scope); wrong argument shapes like name(Value) without quotes or skip_impl(true) instead of the bare flag; the same key given twice; omitting the required category(...) entirely.

Common situations: Typos in attribute names (e.g. categry("Value")); copying example code whose keys changed across a macro version bump; IDE autocompletion inserting parentheses where a path-only flag is expected.

Understand the failure class

Related errors


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