GraphiteEditor/Graphite · error · syn::Error

Expected MessageHandler trait

Error message

Expected MessageHandler trait

What it means

The trait being implemented under `#[message_handler_data]` must literally end in a segment named `MessageHandler` (i.e. `MessageHandler<M, C>`). This error is raised when the last path segment of the trait is a different name, because the macro cannot pull the message and context types from its angle-bracketed arguments.

Source

Thrown at proc-macros/src/message_handler_data_attr.rs:30

	let path = match &**self_ty {
		Type::Path(path) => &path.path,
		_ => return Err(syn::Error::new(Span::call_site(), "Expected impl implementation")),
	};

	let input_type = path.segments.last().map(|s| &s.ident).unwrap();

	let handler_line_number = input_type.span().start().line;

	// Extract the message type from the trait path
	let trait_path = match &impl_block.trait_ {
		Some((_, path, _)) => path,
		None => return Err(syn::Error::new(Span::call_site(), "Expected trait implementation")),
	};

	// Get the trait generics (should be MessageHandler<M, C>)
	if let Some(segment) = trait_path.segments.last() {
		if segment.ident != "MessageHandler" {
			return Err(syn::Error::new(segment.ident.span(), "Expected MessageHandler trait"));
		}
		if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
			if args.args.len() >= 2 {
				// Extract the message type (M) and context struct type (C) from the trait params
				let message_type = &args.args[0];
				let data_type = &args.args[1];

				let impl_item = match data_type {
					syn::GenericArgument::Type(t) => {
						match t {
							syn::Type::Path(type_path) if !type_path.path.segments.is_empty() => {
								// Get just the base identifier (ToolMessageData) without generics
								let type_name = &type_path.path.segments.first().unwrap().ident;

								let handler_data_line_number = type_name.span().start().line;

								quote! {
									#input_item

View on GitHub (pinned to c507b35645)

Solutions

  1. Make the impl target the real trait: `impl MessageHandler<M, C> for MyHandler { … }`.
  2. If you aliased the trait (`use x::MessageHandler as MH;`), use the original name in the impl so the segment matches.
  3. Remove the attribute from impls of unrelated traits — they don't need message-handler registration.

Example fix

// before
#[message_handler_data]
impl SomeOtherTrait for BrushTool { ... }

// after
#[message_handler_data]
impl MessageHandler<BrushMessage, BrushToolMessageData> for BrushTool { ... }
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: Writing `#[message_handler_data] impl SomeOtherTrait for MyHandler { … }`, importing MessageHandler under an alias so the segment name differs, or using a re-exported/renamed trait path whose final segment is not exactly `MessageHandler`.

Common situations: Copy-pasting an impl block from another trait integration, trait aliases or wrapper traits added during a refactor, or namespaced paths like `handlers::MessageHandler` are fine only if the last segment still reads `MessageHandler`.

Related errors


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