dotnet/wpf · error · ArgumentException

SR.HandlerTypeIllegal

Error message

SR.HandlerTypeIllegal

What it means

UIElement.AddHandler throws ArgumentException with SR.HandlerTypeIllegal when the supplied handler delegate is not of a type the routed event considers legal. RoutedEvent.IsLegalHandler checks that the handler's type matches the event's registered handler type (e.g. RoutedEventHandler) or is a compatible delegate. This prevents wiring a delegate with the wrong signature to a routed event, which would fail at invoke time.

Solutions

  1. Use a handler delegate whose type exactly matches the type the RoutedEvent was registered with (the handlerType argument to RoutedEvent.Register).
  2. If you intended the generic handler, use RoutedEvent.AddHandler(owner, handler) overload semantics or re-register the event with RoutedEventHandler as its handler type.
  3. Use the CLR event wrapper (e.g. element.MouseDown += Handler) and let the compiler enforce the delegate type.
  4. Verify routedEvent.HandlerType at runtime if the event is received dynamically, and construct the handler via Delegate.CreateDelegate with that type.

Example fix

// before
var handler = new EventHandler((s, e) => { });
elem.AddHandler(MyControl.MyCustomEvent, handler); // ArgumentException
// after
var handler = new MyCustomRoutedEventHandler((s, e) => { });
elem.AddHandler(MyControl.MyCustomEvent, handler);
Defensive patterns

Strategy: validation

Validate before calling

if (routedEvent == null) throw new ArgumentNullException(nameof(routedEvent));
if (handler == null) throw new ArgumentNullException(nameof(handler));
if (!routedEvent.IsLegalHandler(handler))
    throw new ArgumentException($"Handler must be of type {routedEvent.HandlerType} for this routed event.");

Type guard

static bool IsLegalFor(RoutedEvent ev, Delegate h) => h != null && ev != null && ev.IsLegalHandler(h);

Try / catch

try { element.AddHandler(routedEvent, handler); }
catch (ArgumentException ex) when (ex.Message.Contains("HandlerTypeIllegal") || ex.ParamName == null) { /* log wrong delegate type, use correct handler type */ }

Prevention

When it happens

Trigger: Calling uiElement.AddHandler(routedEvent, handler) where handler is a Delegate whose type is not compatible with routedEvent.HandlerType — e.g. passing a MyCustomEventHandler delegate to an event registered for RoutedEventHandler, or passing a generic/unrelated delegate instance.

Common situations: Developers create a custom routed event with a specific handler type but then attach a generic RoutedEventHandler or a lambda cast to the wrong delegate type; refactors change the handler type of a RoutedEvent.Register call but old call sites still use the old delegate type.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Generated/UIElement.cs:521

        /// </param>
        /// <param name="handledEventsToo">
        ///     Flag indicating whether or not the listener wants to
        ///     hear about events that have already been handled
        /// </param>
        public void AddHandler(
            RoutedEvent routedEvent,
            Delegate handler,
            bool handledEventsToo)
        {
            // VerifyAccess();

            ArgumentNullException.ThrowIfNull(routedEvent);

            ArgumentNullException.ThrowIfNull(handler);

            if (!routedEvent.IsLegalHandler(handler))
            {
                throw new ArgumentException(SR.HandlerTypeIllegal);
            }

            EnsureEventHandlersStore();
            EventHandlersStore.AddRoutedEventHandler(routedEvent, handler, handledEventsToo);

            OnAddHandler(routedEvent, handler);
        }

        /// <summary>
        ///     Notifies subclass of a new routed event handler.  Note that this is
        ///     called once for each handler added, but OnRemoveHandler is only called
        ///     on the last removal.
        /// </summary>
        internal virtual void OnAddHandler(
            RoutedEvent routedEvent,
            Delegate handler)
        {
        }

View on GitHub (pinned to 81131a70a4)