{"record":{"id":"d2f164d62b08fe04","repo":"dotnet/wpf","slug":"sr-undounitcantbeaddedtwice","errorCode":null,"errorMessage":"SR.UndoUnitCantBeAddedTwice","messagePattern":"SR\\.UndoUnitCantBeAddedTwice","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/ParentUndoUnit.cs","lineNumber":225,"sourceCode":"        /// </exception>\n        public virtual void Add(IUndoUnit unit)\n        {\n            IParentUndoUnit parentUndoUnit;\n\n            ArgumentNullException.ThrowIfNull(unit);\n\n            parentUndoUnit = DeepestOpenUnit;\n\n            // If we have an open unit, call Add on it\n            if (parentUndoUnit != null)\n            {\n                parentUndoUnit.Add(unit);\n                return;\n            }\n\n            if (IsInParentUnitChain(unit))\n            {\n                throw new InvalidOperationException(SR.UndoUnitCantBeAddedTwice);\n            }\n\n            if (Locked)\n            {\n                throw new InvalidOperationException(SR.UndoUnitLocked);\n            }\n\n            if (!Merge(unit))\n            {\n                _units.Push(unit);\n                if (LastUnit is IParentUndoUnit)\n                {\n                    ((IParentUndoUnit)LastUnit).OnNextAdd();\n                }\n\n                SetLastUnit(unit);\n            }\n        }","sourceCodeStart":207,"sourceCodeEnd":243,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/ParentUndoUnit.cs#L207-L243","documentation":"ParentUndoUnit.Add pushes a completed undo unit onto this parent's unit stack. A unit that is already part of this parent's chain (IsInParentUnitChain) must not be added again, so InvalidOperationException(SR.UndoUnitCantBeAddedTwice) is thrown to prevent the same unit appearing twice in the undo stack.","triggerScenarios":"Calling Add(unit) with a unit already contained in this parent unit (or an ancestor), typically by closing a unit and re-adding it, or adding a parent to one of its own descendants.","commonSituations":"Custom undo transaction code that both calls Close (which implicitly adds the unit) and then Add explicitly; moving units between stacks without removing them from the original parent; cyclic parent/child wiring.","solutions":["Do not call Add for a unit already closed into this parent — Close already adds it via the internal path.","Remove the unit from its current parent before adding it to a different one.","Track added units (e.g. a HashSet) and skip duplicates in custom undo code.","If merging behavior is intended, rely on the Merge mechanism rather than re-adding."],"exampleFix":"// before\nparentUnit.Close(unit, action);\nparentUnit.Add(unit); // duplicate add\n// after\nparentUnit.Close(unit, action); // Close already registers the unit","handlingStrategy":"validation","validationCode":"bool alreadyRegistered = addedUnits.Contains(unit);\nif (!alreadyRegistered) parentUnit.Add(unit);","typeGuard":"bool IsNotInChain(ParentUndoUnit p, IUndoUnit u) => !p.IsInParentUnitChain(u);","tryCatchPattern":"try { parentUnit.Add(unit); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"added twice\"))\n{\n    // unit already on the stack: ignore\n}","preventionTips":["Remember Close implicitly registers the unit — do not Add afterwards","Remove a unit from its old parent before re-parenting","Keep a HashSet of added units in custom undo code"],"tags":["wpf","undo-unit","duplicate","invalid-state"],"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"}