dotnet/wpf · error · InvalidOperationException
SR.UndoManagerAlreadyAttached
Error message
SR.UndoManagerAlreadyAttached
What it means
UndoManager.AttachUndoManager throws InvalidOperationException(SR.UndoManagerAlreadyAttached) when the supplied UndoManager instance is already bound to another scope (its _scope is non-null). Each UndoManager can serve exactly one PropertyEditor/IScope at a time; attaching the same instance to a second scope would corrupt undo-unit bookkeeping, so WPF rejects it. Detach first or create a new UndoManager.
Solutions
- Call UndoManager.DetachUndoManager(oldScope) (or Detach on the manager) before attaching it to a new scope.
- Create a new UndoManager instance for each scope instead of sharing one.
- Check ((UndoManager)undoManager)._scope != null (or expose/track attachment state) before attaching and route accordingly.
- Ensure error paths that abort attach logic still detach the manager to avoid leaked scope bindings.
Example fix
// before
UndoManager.AttachUndoManager(newScope, sharedUndoManager); // throws if already attached
// after
var existingScope = ((UndoManager)sharedUndoManager).Scope;
if (existingScope != null)
{
UndoManager.DetachUndoManager(existingScope);
}
UndoManager.AttachUndoManager(newScope, sharedUndoManager); Defensive patterns
Strategy: validation
Validate before calling
static bool CanAttach(object undoManager) => undoManager is UndoManager um && um._scope == null; // or track attachment externally
Type guard
static bool IsAttachable(UndoManager um) => um != null && um.GetType().GetProperty("Scope", BindingFlags.NonPublic | BindingFlags.Instance) == null; // prefer wrapping with your own attached-state flag Try / catch
try { UndoManager.AttachUndoManager(scope, um); }
catch (InvalidOperationException) { UndoManager.DetachUndoManager(previousScope); UndoManager.AttachUndoManager(scope, um); } Prevention
- Never share one UndoManager across multiple scopes; allocate per scope
- Always detach in teardown/cleanup paths symmetric to attach
- Track ownership of UndoManager instances in your control infrastructure
- Catch InvalidOperationException and detach-then-retry when recycling is intentional
When it happens
Trigger: Calling UndoManager.AttachUndoManager(scope, undoManager) where undoManager was previously attached to a different (or the same) scope and never detached — e.g. re-using one UndoManager across two TextBox scopes or re-attaching after a failed teardown.
Common situations: Re-using a cached UndoManager instance when recreating controls; attaching undo managers in a loop to multiple property scopes; framework code paths (e.g. editing services) that assume a fresh UndoManager but receive a recycled one.
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
- E_FAIL
- FILTER_E_ACCESS
- InvalidOperationException
- InvalidOperationException (no message)
- SR.CannotChangePublishLicense
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/b87c63983664cbc7.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/UndoManager.cs:100
#region Internal Methods
/// <summary>
/// Defines a given FrameworkElement as a scope for undo service.
/// New instance of UndoManager created and attached to this element.
/// </summary>
/// <param name="scope">
/// FrameworkElement to which new instance of UndoManager is attached.
/// </param>
/// <param name="undoManager">
/// </param>
internal static void AttachUndoManager(DependencyObject scope, UndoManager undoManager)
{
ArgumentNullException.ThrowIfNull(scope);
ArgumentNullException.ThrowIfNull(undoManager);
if (undoManager is not null && ((UndoManager)undoManager)._scope != null)
{
throw new InvalidOperationException(SR.UndoManagerAlreadyAttached);
}
// Detach existing instance of undo manager if any
DetachUndoManager(scope);
// Attach the service to the scope via private dependency property
scope.SetValue(UndoManager.UndoManagerInstanceProperty, undoManager);
if (undoManager is not null)
{
Debug.Assert(((UndoManager)undoManager)._scope == null);
((UndoManager)undoManager)._scope = scope;
}
undoManager.IsEnabled = true;
}
/// <summary>
/// Detaches an undo service from the given FrameworkElement.View on GitHub (pinned to 81131a70a4)