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
- Do not change RoutedEvent inside a handler; instead call RaiseEvent with a new RoutedEventArgs configured with the desired RoutedEvent.
- Clone/reconstruct the args: new MouseEventArgs(...) with the target RoutedEvent and raise it explicitly.
- Mark the original args handled (e.Handled = true) and raise the substitute event separately.
- 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
- Treat RoutedEventArgs.RoutedEvent as immutable during handler execution.
- Raise new, properly configured args instead of re-targeting existing ones.
- Mark original events handled and raise substitute events separately.
- Avoid reusing RoutedEventArgs instances across multiple RaiseEvent calls.
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
- SR.RoutedEventArgsMustHaveRoutedEvent
- ArgumentOutOfRangeException(routedEvent)
- Current DocumentSequence, FixedDocument, or FixedPage not…
- IAmbientProvider
- InvalidEnumArgumentException: routingStrategy
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)