{"record":{"id":"6410e874f8bc40b1","repo":"dotnet/wpf","slug":"sr-undonotinnormalstate","errorCode":null,"errorMessage":"SR.UndoNotInNormalState","messagePattern":"SR\\.UndoNotInNormalState","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/UndoManager.cs","lineNumber":514,"sourceCode":"        /// <exception cref=\"ArgumentOutOfRangeException\">\n        /// Thrown if count is out of range\n        /// </exception>\n        /// <exception cref=\"Exception\">\n        /// Thrown if there's an error performing the undo\n        /// </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;","sourceCodeStart":496,"sourceCodeEnd":532,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/UndoManager.cs#L496-L532","documentation":"UndoManager.Undo throws this when the manager's State is not UndoState.Normal — i.e. an Undo or Redo pass is already running (State == Undo/Redo). Undo operations cannot be nested or interleaved because the manager mutates a single state machine while replaying units.","triggerScenarios":"Calling Undo while a previous Undo/Redo call is still executing (State != UndoState.Normal), e.g. re-entrant calls triggered by unit.Do()/event handlers raised during undo replay, or invoking Undo from a message box/event handler fired inside an ongoing undo.","commonSituations":"Custom undo units whose Do() implementation raises property-change events that trigger another Undo; automation scripts issuing Undo commands faster than replay completes; a crash inside a unit leaving State stuck and subsequent calls failing.","solutions":["Check State == UndoState.Normal before calling Undo; queue the request if an undo/redo is in progress.","Avoid triggering undo/redo re-entrantly from inside IUndoUnit.Do() or from events raised during replay — defer via Dispatcher.BeginInvoke.","Make unit implementations idempotent and event-safe so replay does not recursively invoke undo.","If State is stuck after an exception, recreate or reset the undo manager instead of retrying."],"exampleFix":"// before\nundoManager.Undo(1); // throws if state != Normal\n// after\nif (undoManager.State == UndoState.Normal)\n{\n    undoManager.Undo(1);\n}\nelse\n{\n    Dispatcher.BeginInvoke(() => undoManager.Undo(1)); // defer until replay finishes\n}","handlingStrategy":"validation","validationCode":"if (undoManager.State == UndoState.Normal) { undoManager.Undo(1); }","typeGuard":"static bool IsIdle(UndoManager m) => m.State == UndoState.Normal;","tryCatchPattern":"try { undoManager.Undo(1); }\ncatch (InvalidOperationException) { Dispatcher.BeginInvoke(() => undoManager.Undo(1)); }","preventionTips":["Never call undo re-entrantly from IUndoUnit.Do() or replay-raised events","Defer queued undo requests via the dispatcher until state returns to Normal","Reset the manager if state appears stuck after an exception"],"tags":["wpf","undo","reentrancy","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-22T01:17:13.364Z"}