{"record":{"id":"ca4acc18a59bc83a","repo":"dotnet/wpf","slug":"sr-undounitopen","errorCode":null,"errorMessage":"SR.UndoUnitOpen","messagePattern":"SR\\.UndoUnitOpen","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/UndoManager.cs","lineNumber":519,"sourceCode":"        /// </exception>\n        internal void Undo(int count)\n        {\n            if (!IsEnabled)\n            {\n                throw new InvalidOperationException(SR.UndoServiceDisabled);\n            }\n\n            ArgumentOutOfRangeException.ThrowIfGreaterThan(count, UndoCount);\n            ArgumentOutOfRangeException.ThrowIfNegativeOrZero(count);\n\n            if (State != UndoState.Normal)\n            {\n                throw new InvalidOperationException(SR.UndoNotInNormalState);\n            }\n\n            if (OpenedUnit != null)\n            {\n                throw new InvalidOperationException(SR.UndoUnitOpen);\n            }\n\n            Invariant.Assert(UndoCount > _minUndoStackCount);\n\n            SetState(UndoState.Undo);\n\n            bool exceptionThrown = true;\n\n            try\n            {\n                while (count > 0)\n                {\n                    IUndoUnit unit;\n\n                    unit = PopUndoStack();\n                    unit.Do();\n                    count--;\n                }","sourceCodeStart":501,"sourceCodeEnd":537,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/UndoManager.cs#L501-L537","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure every Open() has a matching Close() in a finally block so OpenedUnit returns to null before undo is requested.","Defer Undo calls raised during recording (text-change events) until after the unit closes.","If a unit was left open by an exception, close/reset the manager to clear OpenedUnit before undoing.","Do not nest user-triggered Undo inside code that is creating undo units."],"exampleFix":"// before\nundoManager.Open(unit);\nMakeEdit();\nundoManager.Undo(1); // throws: unit still open\nundoManager.Close();\n// after\nundoManager.Open(unit);\ntry { MakeEdit(); }\nfinally { undoManager.Close(); }\nundoManager.Undo(1);","handlingStrategy":"validation","validationCode":"if (undoManager.OpenedUnit == null && undoManager.State == UndoState.Normal) { undoManager.Undo(1); }","typeGuard":"static bool CanUndoNow(UndoManager m) => m.OpenedUnit == null && m.State == UndoState.Normal;","tryCatchPattern":null,"preventionTips":["Close open units in finally blocks","Defer undo requested during unit recording","Recover leaked open units promptly"],"tags":["wpf","undo","invalid-state","lifecycle"],"backgroundTag":"invalid-state-transition","analyzedSha":"81131a70a4c573cd62748a5c36908fc4d662daa9","analyzedAt":"2026-09-14T10:12:48.479Z","contentChangedAt":"2026-09-14T10:12:48.479Z","schemaVersion":2},"datasetVersion":"2026-09-21T21:30:21.729Z"}