dotnet/wpf · error · InvalidOperationException
SR.UndoUnitCantBeOpenedTwice
Error message
SR.UndoUnitCantBeOpenedTwice
What it means
UndoManager.Open throws this when the unit passed is already the deepest currently open unit (deepestOpen == unit). A single unit cannot be opened twice; nested Open calls must use distinct, newly created parent units.
Solutions
- Create a new IParentUndoUnit instance for each Open call instead of reusing one
- Check UndoManager.OpenedUnit/DeepestOpenUnit before opening and skip if already open
- Guard re-entrant event handlers with a boolean flag while the unit is open
Example fix
// before var unit = _cachedUnit; undoManager.Open(unit); // after var unit = new ParentUndoUnit(undoManager); undoManager.Open(unit);
Defensive patterns
Strategy: validation
Validate before calling
if (undoManager.OpenedUnit == unit || undoManager.DeepestOpenUnit == unit) { /* already open: skip */ } Try / catch
try { undoManager.Open(unit); } catch (InvalidOperationException ex) when (ex.Message.Contains("twice")) { /* unit already open: reuse it */ } Prevention
- Create a fresh ParentUndoUnit per Open call
- Guard re-entrant TextChanged handlers with a flag
- Never cache and reuse opened undo units
When it happens
Trigger: Calling Open with the same IParentUndoUnit instance that is already open, typically from re-entrant calls in Do, Reopen, OpenCompositionUndoUnit, OpenTypingUndoUnit, or ResizeColumn.
Common situations: Re-entrant text change handlers (TextChanged/TextChanging) that open the same undo unit again, or custom code that opens a unit and then triggers an edit that re-opens it.
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.TextBoxBase_CantSetIsUndoEnabledInsideChangeBlock
- SR.TextBoxBase_UnmatchedEndChange
- SR.UndoNoOpenUnit
- SR.UndoServiceDisabled
- SR.UndoUnitAlreadyOpen
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/c2e1d12ed19136c0.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/UndoManager.cs:198
/// </exception>
/// <exception cref="ArgumentNullException">
/// Thrown if passed unit is null.
/// </exception>
internal void Open(IParentUndoUnit unit)
{
IParentUndoUnit deepestOpen;
if (!IsEnabled)
{
throw new InvalidOperationException(SR.UndoServiceDisabled);
}
ArgumentNullException.ThrowIfNull(unit);
deepestOpen = DeepestOpenUnit;
if (deepestOpen == unit)
{
throw new InvalidOperationException(SR.UndoUnitCantBeOpenedTwice);
}
if (deepestOpen == null)
{
if (unit != LastUnit)
{
// Don't want to add the unit again if we're just reopening it
Add(unit as IUndoUnit);
SetLastUnit(unit as IUndoUnit);
}
SetOpenedUnit(unit);
unit.Container = this;
}
else
{
unit.Container = deepestOpen;
deepestOpen.Open(unit);
}View on GitHub (pinned to 81131a70a4)