dotnet/wpf · error · InvalidOperationException
SR.Format(SR.TextEditorCanNotRegisterCommandHandler…
Error message
SR.Format(SR.TextEditorCanNotRegisterCommandHandler, ((Type)_registeredEditingTypes[i]).Name, controlType.Name)
What it means
TextEditor.RegisterCommandHandlers maintains a static list of control types that already registered editing command handlers. If a new controlType is a superclass of an already-registered type, the same handlers would be attached twice, so the registration throws InvalidOperationException naming both types. It is a guard against duplicate/base-class handler registration.
Solutions
- Register only the most-derived control type once; remove the redundant registration.
- Check TextEditor's registered types (or guard with a static bool) before calling RegisterCommandHandlers.
- Move registration to a single point (e.g. static constructor of the leaf type).
Example fix
// before
TextEditor.RegisterCommandHandlers(controlType, ...); // called in both BaseControl and DerivedControl
// after
protected static bool _handlersRegistered;
if (!_handlersRegistered)
{
TextEditor.RegisterCommandHandlers(controlType, ...);
_handlersRegistered = true;
} Defensive patterns
Strategy: validation
Validate before calling
if (TextEditorHelpers.IsRegistered(controlType)) return; // guard before calling RegisterCommandHandlers
Try / catch
try { TextEditor.RegisterCommandHandlers(controlType, ...); }
catch (InvalidOperationException) { /* already registered via a derived type; skip */ } Prevention
- Register command handlers from exactly one type in a hierarchy
- Use a static registration flag
- Prefer registering leaf-most control types only
When it happens
Trigger: A custom control deriving from (or being a base class of) a control type that already called RegisterCommandHandlers calls TextEditor.RegisterCommandHandlers for the same TextEditor instance.
Common situations: Registering editing handlers in a control hierarchy where both a base and derived control attempt registration; registering the same control type twice during app initialization or XAML loading.
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.DuplicateEventName
- SR.PenService_WindowNotRegistered
- SR.TooManyRoutedEvents
- " }} " element found. Expected fixed page element ( }} ).
- ' ' can only host a ' ' or a ' '. ' ' is an invalid…
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/d1c86e4206d16ca3.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/TextEditor.cs:325
{
// Check if we already registered handlers for this type
Invariant.Assert(_registeredEditingTypes != null);
lock (_registeredEditingTypes)
{
for (int i = 0; i < _registeredEditingTypes.Count; i++)
{
// If controlType is or derives from some already registered class - we are done
if (((Type)_registeredEditingTypes[i]).IsAssignableFrom(controlType))
{
return;
}
// Check if controlType is not a superclass of some registered class.
// This is erroneus condition, which must be avoided.
// Otherwise the same handlers will be attached to some class twice.
if (controlType.IsAssignableFrom((Type)_registeredEditingTypes[i]))
{
throw new InvalidOperationException(
SR.Format(SR.TextEditorCanNotRegisterCommandHandler, ((Type)_registeredEditingTypes[i]).Name, controlType.Name));
}
}
// The class was not yet registered. Add it to the list before starting registering handlers.
_registeredEditingTypes.Add(controlType);
}
// Mouse
TextEditorMouse._RegisterClassHandlers(controlType, registerEventListeners);
if (!readOnly)
{
// Typing
TextEditorTyping._RegisterClassHandlers(controlType, registerEventListeners);
}
// Drag-and-drop
TextEditorDragDrop._RegisterClassHandlers(controlType, readOnly, registerEventListeners);
// Cut-Copy-PasteView on GitHub (pinned to 81131a70a4)