dotnet/wpf · error · InvalidOperationException
SR.UndoUnitNotOnTopOfStack
Error message
SR.UndoUnitNotOnTopOfStack
What it means
InvalidOperationException from UndoManager.Reopen: the unit passed for reopening is not the top unit on the relevant stack (undo stack in Normal/Redo state, redo stack in Undo state), so it cannot be reopened out of order.
Solutions
- Only call Reopen with the unit reference that was most recently closed on top of the stack
- Verify UndoCount > 0 and the target unit equals the top of the undo stack before reopening
- If the unit is buried in the stack, abandon reopening and open a fresh unit instead
Example fix
// before
undoManager.Reopen(oldUnit);
// after
if (undoManager.UndoCount > 0 && undoManager.PeekUndoStack() == oldUnit)
{
undoManager.Reopen(oldUnit);
} Defensive patterns
Strategy: validation
Validate before calling
bool canReopen = undoManager.UndoCount > 0 && undoManager.PeekUndoStack() == unit;
Try / catch
try { undoManager.Reopen(unit); } catch (InvalidOperationException ex) when (ex.Message.Contains("top of stack")) { /* open a fresh unit instead */ } Prevention
- Only reopen the most recently closed top-of-stack unit
- Re-read stack top instead of caching unit references
- Open a new unit when the target is buried
When it happens
Trigger: Reopen called while State is Normal or Redo and either UndoCount == 0 or PeekUndoStack() != unit.
Common situations: Trying to reopen a unit that was already merged/closed into another unit, or reopening after other edits pushed new units onto the undo stack.
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.UndoManagerAlreadyAttached
- SR.UndoNoOpenParentUnit
- SR.UndoNoOpenUnit
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/988f684fd5ec507b.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/UndoManager.cs:256
{
throw new InvalidOperationException(SR.UndoServiceDisabled);
}
ArgumentNullException.ThrowIfNull(unit);
if (OpenedUnit != null)
{
throw new InvalidOperationException(SR.UndoUnitAlreadyOpen);
}
switch (State)
{
case UndoState.Normal:
case UndoState.Redo:
{
if (UndoCount == 0 || PeekUndoStack() != unit)
{
throw new InvalidOperationException(SR.UndoUnitNotOnTopOfStack);
}
break;
}
case UndoState.Undo:
{
if (RedoStack.Count == 0 || (IParentUndoUnit)RedoStack.Peek() != unit)
{
throw new InvalidOperationException(SR.UndoUnitNotOnTopOfStack);
}
break;
}
case UndoState.Rollback:
default:
// should only happen if someone changes the UndoState enum or parameter validationView on GitHub (pinned to 81131a70a4)