{"record":{"id":"a4a599babf3de27c","repo":"dotnet/wpf","slug":"sr-undounitcantbeopenedtwice","errorCode":null,"errorMessage":"SR.UndoUnitCantBeOpenedTwice","messagePattern":"SR\\.UndoUnitCantBeOpenedTwice","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/ParentUndoUnit.cs","lineNumber":69,"sourceCode":"        /// </summary>\n        /// <param name=\"newUnit\">\n        /// IParentUndoUnit to open\n        /// </param>\n        /// <exception cref=\"ArgumentNullException\">\n        /// Thrown if passed unit is null.\n        /// </exception>\n        public virtual void Open(IParentUndoUnit newUnit)\n        {\n            IParentUndoUnit deepestOpen;\n\n            ArgumentNullException.ThrowIfNull(newUnit);\n\n            deepestOpen = DeepestOpenUnit;\n            if (deepestOpen == null)\n            {\n                if (IsInParentUnitChain(newUnit))\n                {\n                    throw new InvalidOperationException(SR.UndoUnitCantBeOpenedTwice);\n                }\n\n                _openedUnit = newUnit;\n                newUnit?.Container = this;\n            }\n            else\n            {\n                newUnit?.Container = deepestOpen;\n\n                deepestOpen.Open(newUnit);\n            }\n        }\n\n        /// <summary>\n        /// Closes the current open unit, adding it to the containing unit's undo stack if committed.\n        /// </summary>\n        public virtual void Close(UndoCloseAction closeAction)\n        {","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/ParentUndoUnit.cs#L51-L87","documentation":"ParentUndoUnit.Open starts a new open undo unit nested inside this parent. The same undo unit cannot be opened twice while it is already part of the parent chain: if `newUnit` is found in this unit's parent chain, InvalidOperationException(SR.UndoUnitCantBeOpenedTwice) is thrown to prevent recursive/cyclic opening.","triggerScenarios":"Calling Open(newUnit) on a ParentUndoUnit when newUnit already appears in its parent unit chain (IsInParentUnitChain returns true), i.e. re-opening an already-open or ancestor unit.","commonSituations":"Custom text-editing undo implementations calling Open twice for the same unit without an intervening Close; re-entrant edit handlers (e.g. nested property-change events) opening the same unit again; forgetting that Open fails when no deeper unit is open but the unit is an ancestor.","solutions":["Call Close(unit, ...) for the currently open unit before calling Open on it (or any ancestor) again.","Check OpenedUnit/DeepestOpenUnit state before calling Open and skip if the unit is already open.","Ensure your edit transaction boundaries are balanced: every Open has exactly one matching Close, including in exception paths (try/finally).","If the unit should merge, use Add/Merge instead of opening again."],"exampleFix":"// before\nif (currentUnit != null) parentUndoUnit.Open(currentUnit); // throws if already in chain\n// after\nif (parentUndoUnit.OpenedUnit != currentUnit) parentUndoUnit.Open(currentUnit);","handlingStrategy":"validation","validationCode":"bool canOpen = parentUnit != null\n    && parentUnit.OpenedUnit == null\n    && !ReferenceEquals(parentUnit.OpenedUnit, newUnit);","typeGuard":"bool IsOpenInChain(ParentUndoUnit parent, IUndoUnit unit) =>\n    parent != null && unit != null &&\n    object.ReferenceEquals(parent.DeepestOpenUnit, unit);","tryCatchPattern":"try { parentUnit.Open(newUnit); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"opened twice\"))\n{\n    // unit already open: skip or close the previous unit first\n}","preventionTips":["Balance every Open with exactly one Close using try/finally","Check OpenedUnit before opening","Never open an ancestor unit inside its own descendant"],"tags":["wpf","undo-unit","invalid-state","text-editing"],"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"}