dotnet/wpf · error · ArgumentException

SR.HandlerTypeIllegal

Error message

SR.HandlerTypeIllegal

What it means

AddHandler throws ArgumentException when the handler delegate's concrete type does not exactly match routedEvent.HandlerType. Unlike CLR events, routed events require the delegate type to match exactly (e.g. RoutedEventHandler for Click), not just be compatible via assignment. The check is strict equality of the handler's runtime type.

Solutions

  1. Wrap the method in the exact delegate type: routedEvent.HandlerType, e.g. new RoutedEventHandler(OnClick) for Click.
  2. Check routedEvent.HandlerType at runtime to see the required delegate type.
  3. Declare handler variables with the specific delegate type instead of Delegate/Action.

Example fix

// before
Delegate h = new MouseButtonEventHandler(OnClick);
factory.AddHandler(ButtonBase.ClickEvent, h);
// after
RoutedEventHandler h = OnClick;
factory.AddHandler(ButtonBase.ClickEvent, h);
Defensive patterns

Strategy: type-guard

Validate before calling

if (handler.GetType() != routedEvent.HandlerType)
    throw new ArgumentException($"Handler must be {routedEvent.HandlerType.Name}");

Type guard

static bool IsMatchingHandler(RoutedEvent e, Delegate h) => h.GetType() == e.HandlerType;

Try / catch

try { factory.AddHandler(routedEvent, handler); }
catch (ArgumentException) { var typed = Delegate.CreateDelegate(routedEvent.HandlerType, handler.Target, handler.Method); factory.AddHandler(routedEvent, typed); }

Prevention

When it happens

Trigger: Calling factory.AddHandler(routedEvent, handler) where handler.GetType() != routedEvent.HandlerType — e.g. passing a RoutedEventHandler to an event expecting a MouseButtonEventHandler, or a lambda cast to the wrong delegate type.

Common situations: Reusing one handler method for multiple events and wrapping it in the wrong delegate type; generic or custom delegate declarations that compile but don't match the routed event's HandlerType.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/1e354c5a313f7a1e. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/FrameworkElementFactory.cs:310

            AddHandler(routedEvent, handler, false);
        }

        /// <summary>
        ///     Add an event handler for the given routed event. This action applies to the instances created by this factory
        /// </summary>
        public void AddHandler(RoutedEvent routedEvent, Delegate handler, bool handledEventsToo)
        {
            if (_sealed)
            {
                throw new InvalidOperationException(SR.Format(SR.CannotChangeAfterSealed, "FrameworkElementFactory"));
            }

            ArgumentNullException.ThrowIfNull(routedEvent);
            ArgumentNullException.ThrowIfNull(handler);

            if (handler.GetType() != routedEvent.HandlerType)
            {
                throw new ArgumentException(SR.HandlerTypeIllegal);
            }

            if (_eventHandlersStore == null)
            {
                _eventHandlersStore = new EventHandlersStore();
            }

            _eventHandlersStore.AddRoutedEventHandler(routedEvent, handler, handledEventsToo);

            // Keep track of whether we're listening to the loaded or unloaded events;
            // if so, we have to trigger a listener in the FE/FCE (as a performance
            // optimization).

            if (  (routedEvent == FrameworkElement.LoadedEvent)
                ||(routedEvent == FrameworkElement.UnloadedEvent))
            {
                HasLoadedChangeHandler = true;
            }

View on GitHub (pinned to 81131a70a4)