dotnet/wpf · error · InvalidOperationException

SR.UndoNoOpenUnit

Error message

SR.UndoNoOpenUnit

What it means

ParentUndoUnit.Close(unit, closeAction) closes the currently open child undo unit. If this parent has no OpenedUnit, there is nothing to close, so InvalidOperationException(SR.UndoNoOpenUnit) is thrown to report the unbalanced Open/Close state.

Solutions

  1. Check OpenedUnit != null (or that it equals the unit you intend to close) before calling Close.
  2. Use try/finally so Open/Close remain balanced even when edits throw.
  3. Ensure the unit you pass to Close is the exact unit returned/held from the Open call.
  4. If the manager was reset (UndoManager.Clear/Reset), discard pending unit references instead of closing them.

Example fix

// before
parentUndoUnit.Close(unit, undoCloseAction);
// after
if (parentUndoUnit.OpenedUnit == unit)
{
    parentUndoUnit.Close(unit, undoCloseAction);
}
Defensive patterns

Strategy: validation

Validate before calling

if (unit == null) throw new ArgumentNullException(nameof(unit));
bool closable = parentUnit.OpenedUnit != null;

Type guard

bool HasOpenUnit(ParentUndoUnit p) => p?.OpenedUnit != null;

Try / catch

try { parentUnit.Close(unit, action); }
catch (InvalidOperationException ex) when (ex.Message.Contains("no open unit"))
{
    // unbalanced close: drop the stale reference
}

Prevention

When it happens

Trigger: Calling Close on a ParentUndoUnit (or a unit reached through the parent chain) when OpenedUnit is null — i.e. Close called without a matching Open, or Close called twice for the same unit.

Common situations: Double-close in exception handling paths; closing an undo unit after the undo stack was reset/cleared; mismatched Open/Close pairing across re-entrant text edit events.

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/8b7575a3e4c593f7. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/ParentUndoUnit.cs:114

        /// IParentUndoUnit to close.  If NULL, this unit's OpenedUnit is closed.
        /// </param>
        /// <param name="closeAction">
        /// </param>
        /// <exception cref="InvalidOperationException">
        /// Thrown if no undo unit is currently open
        /// </exception>
        /// <exception cref="ArgumentNullException">
        /// Thrown if unit is null
        /// </exception>
        public virtual void Close(IParentUndoUnit unit, UndoCloseAction closeAction)
        {
            UndoManager undoManager;

            ArgumentNullException.ThrowIfNull(unit);

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

            // find the parent of the given unit
            if (OpenedUnit != unit)
            {
                IParentUndoUnit closeParent;

                closeParent = this;
                while (closeParent.OpenedUnit != null && closeParent.OpenedUnit != unit)
                {
                    closeParent = closeParent.OpenedUnit;
                }

                if (closeParent.OpenedUnit == null)
                {
                    throw new ArgumentException(SR.UndoUnitNotFound, nameof(unit));
                }

View on GitHub (pinned to 81131a70a4)