dotnet/wpf · error · InvalidOperationException

SR.RoutedEventCannotChangeWhileRouting

Error message

SR.RoutedEventCannotChangeWhileRouting

What it means

RoutedEventArgs.RoutedEvent cannot be changed while the event is actively being routed through a handler that was user-initiated. The setter throws InvalidOperationException if UserInitiated && InvokingHandler, because changing the routed event mid-route would corrupt the event pipeline.

Solutions

  1. Do not change RoutedEvent inside a handler; instead call RaiseEvent with a new RoutedEventArgs configured with the desired RoutedEvent.
  2. Clone/reconstruct the args: new MouseEventArgs(...) with the target RoutedEvent and raise it explicitly.
  3. Mark the original args handled (e.Handled = true) and raise the substitute event separately.
  4. If mutation is needed for internal reuse, do it outside handler invocation (before RaiseEvent).

Example fix

// before
protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
{
    e.RoutedEvent = Mouse.MouseDownEvent; // throws during routing
    base.OnMouseDown(e);
}

// after
protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
{
    var args = new MouseButtonEventArgs(e.MouseDevice, e.Timestamp, e.ChangedButton)
    { RoutedEvent = Mouse.MouseDownEvent };
    RaiseEvent(args);
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (args.UserInitiated && args.RoutedEvent != desiredEvent)
{
    // raise a new event instead of mutating
}

Try / catch

try { args.RoutedEvent = otherEvent; }
catch (InvalidOperationException)
{
    // fall back: raise a fresh RoutedEventArgs with the desired RoutedEvent
}

Prevention

When it happens

Trigger: Inside a routed-event handler (or class handler) invoked for a user-initiated event, assigning e.RoutedEvent = someOtherRoutedEvent to try to re-target or re-type the event during routing.

Common situations: Handlers attempting to 'convert' an event (e.g. re-raise PreviewMouseDown as MouseDown) by mutating the args; framework code reusing RoutedEventArgs instances across events; command/bubble interception logic.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/RoutedEventArgs.cs:90

        #endregion Construction

        #region External API
        /// <summary>
        ///     Returns the <see cref="RoutedEvent"/> associated
        ///     with this <see cref="RoutedEventArgs"/>
        /// </summary>
        /// <remarks>
        ///     The <see cref="RoutedEvent"/> cannot be null
        ///     at any time
        /// </remarks>
        public RoutedEvent RoutedEvent
        {
            get {return _routedEvent;}
            set
            {
                if (UserInitiated && InvokingHandler)
                    throw new InvalidOperationException(SR.RoutedEventCannotChangeWhileRouting);

                _routedEvent = value;
            }
        }

        /// <summary>
        ///     Changes the RoutedEvent assocatied with these RoutedEventArgs
        /// </summary>
        /// <remarks>
        ///     Only used internally.  Added to support cracking generic MouseButtonDown/Up events
        ///     into MouseLeft/RightButtonDown/Up events.
        /// </remarks>
        /// <param name="newRoutedEvent">
        ///     The new RoutedEvent to associate with these RoutedEventArgs
        /// </param>
        internal void OverrideRoutedEvent( RoutedEvent newRoutedEvent )
        {
            _routedEvent = newRoutedEvent;

View on GitHub (pinned to 81131a70a4)