dotnet/wpf · error · InvalidOperationException
SR.TooManyRoutedEvents
Error message
SR.TooManyRoutedEvents
What it means
GlobalEventManager.GetNextAvailableGlobalIndex throws InvalidOperationException with SR.TooManyRoutedEvents when the global static counter of registered routed events would overflow int.MaxValue. RoutedEvents are meant to be registered once from static constructors, so the space should never be exhausted in correct usage. The explicit check catches the pathological case of registering routed events from instance code at scale instead of letting the index silently malfunction.
Solutions
- Register each RoutedEvent once in a static constructor or as a static readonly field and reuse it.
- Refactor code that registers events per-instance to share a single static RoutedEvent per event identity.
- Audit dynamic registration paths (factories, plugins) and cache RoutedEvents by name/ownerType.
Example fix
// before
public RoutedEvent GetEvent() => RoutedEvent.Register("Ev", RoutingStrategy.Bubble, typeof(EventHandler), typeof(MyClass)); // new index every call
// after
private static readonly RoutedEvent EvEvent = RoutedEvent.Register("Ev", RoutingStrategy.Bubble, typeof(EventHandler), typeof(MyClass));
public RoutedEvent GetEvent() => EvEvent; Defensive patterns
Strategy: validation
Validate before calling
// Never register routed events per-instance; hoist to a static field:
private static readonly RoutedEvent EvEvent =
RoutedEvent.Register("Ev", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyClass)); Try / catch
try { var ev = RoutedEvent.Register(name, strategy, handlerType, ownerType); }
catch (InvalidOperationException ex) when (ex.Message.Contains("TooManyRoutedEvents")) { /* registration site runs per-instance; fix to static registration */ } Prevention
- Declare RoutedEvents only in static constructors or static readonly fields
- Cache RoutedEvents in a dictionary keyed by (name, ownerType) for dynamic scenarios
- Never call RoutedEvent.Register inside instance methods, factories, or per-item loops
When it happens
Trigger: Registering more than ~2.1 billion RoutedEvent instances, typically by calling RoutedEvent.Register (which allocates a new global index) repeatedly from instance methods or per-object code instead of once statically.
Common situations: A factory class or data template creating RoutedEvents per instance/per item; a plugin system re-registering events on every load; misuse of EventManager.RegisterRoutedEvent outside static initialization.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- SR.DuplicateEventName
- args
- ArgumentOutOfRangeException(routedEvent)
- InvalidEnumArgumentException: routingStrategy
- SR.ClassTypeIllegal
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/91d72241b68d5950.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/GlobalEventManager.cs:439
#endregion Operations
#region Global Index for RoutedEvent and EventPrivateKey
/// <summary>
/// Increments the global counter for <see cref="RoutedEvent"/> and <see cref="EventPrivateKey"/> storage.
/// </summary>
/// <returns>Globally unique index for the event within the application.</returns>
/// <exception cref="InvalidOperationException">Thrown in case the index is bigger than <see cref="int.MaxValue"/>.</exception>
internal static int GetNextAvailableGlobalIndex()
{
// Prevent GlobalIndex from overflow. RoutedEvents are meant to be static members and are to be registered
// only via static constructors. However there is no cheap way of ensuring this, without having to do a stack walk. Hence
// concievably people could register RoutedEvents via instance methods and therefore cause the GlobalIndex to
// overflow. This check will explicitly catch this error, instead of silently malfuntioning.
uint newIndex = Interlocked.Increment(ref s_globalEventIndex);
if (newIndex >= int.MaxValue)
throw new InvalidOperationException(SR.TooManyRoutedEvents);
return (int)newIndex;
}
/// <summary>
/// Access must be done atomically, currently only accessed via <see cref="GetNextAvailableGlobalIndex"/> method.
/// </summary>
private static uint s_globalEventIndex = uint.MinValue;
#endregion
#region Data
// This is an efficient Hashtable of ItemLists keyed on DType
// Each ItemList holds the registered RoutedEvents for that OwnerType
private static DTypeMap _dTypedRoutedEventList = new DTypeMap(10); // Initialization sizes based on typical MSN scenario
// This is a Hashtable of ItemLists keyed on OwnerTypeView on GitHub (pinned to 81131a70a4)