{"record":{"id":"cbd8368ffc87fbdc","repo":"dotnet/wpf","slug":"sr-enumeratorversionchanged-rangecontentenumerator","errorCode":null,"errorMessage":"SR.EnumeratorVersionChanged","messagePattern":"SR\\.EnumeratorVersionChanged","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/RangeContentEnumerator.cs","lineNumber":90,"sourceCode":"                if (_currentCache != null)\n                {\n                    return _currentCache;\n                }\n\n                if (_navigator.CompareTo(_end) >= 0)\n                {\n                    // IEnumerator.Current is documented to throw this exception\n                    throw new InvalidOperationException(SR.EnumeratorReachedEnd);\n                }\n\n                // Throw if the tree has been modified since this enumerator was created unless a tree walk\n                // by the property system is in progress. For example, changing DataContext on FlowDocument\n                // can invalidate a binding on Run.Text during the inherited property change tree walk,\n                // which in turn can modify the TextContainer.\n                if (_generation != _start.TextContainer.Generation && !IsLogicalChildrenIterationInProgress)\n                {\n                    // IEnumerator.Current is documented to throw this exception\n                    throw new InvalidOperationException(SR.EnumeratorVersionChanged);\n                }\n\n                switch (_navigator.GetPointerContext(LogicalDirection.Forward))\n                {\n                    case TextPointerContext.Text:\n                        offset = 0;\n\n                        // Merge all successive text runs into a single value.\n                        do\n                        {\n                            runLength = _navigator.GetTextRunLength(LogicalDirection.Forward);\n                            EnsureBufferCapacity(offset + runLength);\n                            _navigator.GetTextInRun(LogicalDirection.Forward, _buffer, offset, runLength);\n                            offset += runLength;\n                            _navigator.MoveToNextContextPosition(LogicalDirection.Forward);\n                        }\n                        while (_navigator.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text);\n","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/RangeContentEnumerator.cs#L72-L108","documentation":"RangeContentEnumerator implements IEnumerator and throws InvalidOperationException (SR.EnumeratorVersionChanged) when the underlying TextContainer's Generation has changed since the enumerator was created. The enumerator is invalidated by any modification to the text tree (insert/delete) because positions it holds may no longer be valid. An exception is made when a property-system tree walk is in progress (IsLogicalChildrenIterationInProgress), since inherited property changes can legitimately modify the TextContainer mid-iteration.","triggerScenarios":"Calling Current on a RangeContentEnumerator after any edit was made to the associated TextContainer (e.g. Run.Text binding invalidated by a DataContext change on FlowDocument, or user/code edits to the document) between MoveNext calls.","commonSituations":"Iterating document content while the document is being modified: data binding updates text during a DataContext change, asynchronous edits, or re-entrant modification from within the enumeration loop.","solutions":["Complete the enumeration before modifying the TextContainer, or take a snapshot of the content (e.g. TextRange.Text) before editing","Defer document modifications until after iteration completes (e.g. Dispatcher.BeginInvoke)","Re-create the enumerator after the document changes instead of reusing the stale one","Wrap the enumeration in try-catch for InvalidOperationException and restart enumeration with a fresh enumerator"],"exampleFix":"// before\nforeach (var item in textRange.GetContentEnumerator()) { Process(item); ModifyDocument(); }\n// after\nvar snapshot = CollectAll(textRange.GetContentEnumerator());\nforeach (var item in snapshot) { Process(item); }\nModifyDocument();","handlingStrategy":"try-catch","validationCode":"// no public generation API; guard by snapshotting before use\nvar snapshot = new List<object>();\nvar e = textRange.GetContentEnumerator();\nwhile (e.MoveNext()) snapshot.Add(e.Current);","typeGuard":null,"tryCatchPattern":"try { while (e.MoveNext()) Process(e.Current); }\ncatch (InvalidOperationException) { e = textRange.GetContentEnumerator(); /* retry with fresh enumerator */ }","preventionTips":["Never modify the document inside an enumeration loop","Snapshot content before editing","Defer edits via Dispatcher.BeginInvoke until iteration finishes"],"tags":["wpf","enumerator","invalidated-state","textcontainer"],"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"}