dotnet/wpf · error · InvalidOperationException

SR.UndoUnitOpen

Error message

SR.UndoUnitOpen

What it means

UndoManager.Undo throws this when OpenedUnit is non-null, meaning a parent undo unit is currently open (a unit is being recorded). Undo cannot run while a unit is open because the open unit's contents are not finalized and the undo stack is in an inconsistent state.

Solutions

  1. Ensure every Open() has a matching Close() in a finally block so OpenedUnit returns to null before undo is requested.
  2. Defer Undo calls raised during recording (text-change events) until after the unit closes.
  3. If a unit was left open by an exception, close/reset the manager to clear OpenedUnit before undoing.
  4. Do not nest user-triggered Undo inside code that is creating undo units.

Example fix

// before
undoManager.Open(unit);
MakeEdit();
undoManager.Undo(1); // throws: unit still open
undoManager.Close();
// after
undoManager.Open(unit);
try { MakeEdit(); }
finally { undoManager.Close(); }
undoManager.Undo(1);
Defensive patterns

Strategy: validation

Validate before calling

if (undoManager.OpenedUnit == null && undoManager.State == UndoState.Normal) { undoManager.Undo(1); }

Type guard

static bool CanUndoNow(UndoManager m) => m.OpenedUnit == null && m.State == UndoState.Normal;

Prevention

When it happens

Trigger: Calling Undo(count) between Open(parentUnit) and Close() — e.g. undo invoked from a text-change event fired while an edit unit is being recorded, or leaked open unit from an exception that skipped Close.

Common situations: Programmatic edits wrapped in open undo units while also invoking the Undo command; a prior exception in unit recording left the unit open forever, breaking all later Undo calls; automated tests doing 'type text then undo' without closing recording scopes.

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


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/ca4acc18a59bc83a. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/UndoManager.cs:519

        /// </exception>
        internal void Undo(int count)
        {
            if (!IsEnabled)
            {
                throw new InvalidOperationException(SR.UndoServiceDisabled);
            }

            ArgumentOutOfRangeException.ThrowIfGreaterThan(count, UndoCount);
            ArgumentOutOfRangeException.ThrowIfNegativeOrZero(count);

            if (State != UndoState.Normal)
            {
                throw new InvalidOperationException(SR.UndoNotInNormalState);
            }

            if (OpenedUnit != null)
            {
                throw new InvalidOperationException(SR.UndoUnitOpen);
            }

            Invariant.Assert(UndoCount > _minUndoStackCount);

            SetState(UndoState.Undo);

            bool exceptionThrown = true;

            try
            {
                while (count > 0)
                {
                    IUndoUnit unit;

                    unit = PopUndoStack();
                    unit.Do();
                    count--;
                }

View on GitHub (pinned to 81131a70a4)