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
- Read the appended inner error — it names the real problem (e.g. "Unsupported attribute in `node`" or "The attribute 'category' is required")
- 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
- 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
- Start from a working node's attribute block and modify it incrementally
- category(...) is mandatory — add it first; category("") hides the node
- Use only the supported keys: category, name, path, skip_impl, properties, cfg, shader_node, serialize, memoize, inject_scope
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
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Failed to parse input type for #[implementation(...)]. Expec
- Expected `->` arrow after input type in #[implementations(..
- Failed to parse output type for #[implementation(...)]. Expe
- Failed to parse implementations for argument '{name}': {e}
- Invalid #[implementations(...)] for argument `{}`. Expected
AI-assisted analysis of GraphiteEditor/Graphite@c507b35645 (2026-08-16).
Data as JSON: /api/errors/c6d0ed1ce876c52c.
Report an issue: GitHub.