{"record":{"id":"8a3336195c7308db","repo":"dotnet/wpf","slug":"sr-enumeratorversionchanged-collectionview","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/Data/CollectionView.cs","lineNumber":1630,"sourceCode":"        #region Internal Types\n\n        internal class PlaceholderAwareEnumerator : IEnumerator\n        {\n            private enum Position { BeforePlaceholder, OnPlaceholder, OnNewItem, AfterPlaceholder}\n\n            public PlaceholderAwareEnumerator(CollectionView collectionView, IEnumerator baseEnumerator, NewItemPlaceholderPosition placeholderPosition, object newItem)\n            {\n                _collectionView = collectionView;\n                _timestamp = collectionView.Timestamp;\n                _baseEnumerator = baseEnumerator;\n                _placeholderPosition = placeholderPosition;\n                _newItem = newItem;\n            }\n\n            public bool MoveNext()\n            {\n                if (_timestamp != _collectionView.Timestamp)\n                    throw new InvalidOperationException(SR.EnumeratorVersionChanged);\n\n                switch (_position)\n                {\n                    case Position.BeforePlaceholder:\n                        // AtBeginning - move to the placeholder\n                        if (_placeholderPosition == NewItemPlaceholderPosition.AtBeginning)\n                        {\n                            _position = Position.OnPlaceholder;\n                        }\n                        // None or AtEnd - advance base, skipping the new item\n                        else if (_baseEnumerator.MoveNext() &&\n                                    (_newItem == NoNewItem || _baseEnumerator.Current != _newItem\n                                            || _baseEnumerator.MoveNext()))\n                        {\n                        }\n                        // if base has reached the end, move to new item or placeholder\n                        else if (_newItem != NoNewItem)\n                        {","sourceCodeStart":1612,"sourceCodeEnd":1648,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Data/CollectionView.cs#L1612-L1648","documentation":"The view's enumerator (the IEnumerator returned for a CollectionView) throws InvalidOperationException (SR.EnumeratorVersionChanged) when MoveNext (or Reset) is called after the view was refreshed — the view's Timestamp changed, invalidating outstanding enumerators. This prevents enumerating over a view whose contents were rebuilt mid-iteration.","triggerScenarios":"Holding an IEnumerator from a CollectionView (e.g. from foreach with a yield, or stored enumerator) across a call to Refresh(), Filter/SortDescriptions change, or any RefreshInternal; the next MoveNext detects the timestamp mismatch.","commonSituations":"Async code iterating a view while the UI refreshes it; storing enumerators in fields; modifying Filter inside a loop that is enumerating the same view; LINQ deferred evaluation over a view that gets refreshed between iterations.","solutions":["Materialize the view into a snapshot (ToList/ToArray) before long-running or re-entrant iteration","Do not Refresh the view while an enumerator over it is still active","Re-obtain the enumerator after each Refresh instead of caching it","Use a using (DeferRefresh) around operations if the enumeration must complete first (then refresh after)"],"exampleFix":"// before\nvar e = view.GetEnumerator();\nview.Refresh();\ne.MoveNext(); // InvalidOperationException: version changed\n// after\nvar snapshot = view.Cast<object>().ToList();\nview.Refresh();\nforeach (var item in snapshot) { }","handlingStrategy":"validation","validationCode":"var snapshot = view.Cast<object>().ToArray(); // stable across Refresh\nforeach (var item in snapshot) { /* safe */ }","typeGuard":null,"tryCatchPattern":"try { e.MoveNext(); }\ncatch (InvalidOperationException) { e = view.GetEnumerator(); /* retry with fresh enumerator */ }","preventionTips":["Never cache enumerators of a CollectionView across Refresh calls","Snapshot before async or re-entrant work","Avoid mutating Filter/SortDescriptions while enumerating the view"],"tags":["wpf","collectionview","enumerator","collection-modified"],"backgroundTag":"collection-modified-during-enumeration","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"}