{"record":{"id":"b237503a703b847d","repo":"dotnet/wpf","slug":"sr-enumerator-collectionchanged-visualcollection","errorCode":null,"errorMessage":"SR.Enumerator_CollectionChanged","messagePattern":"SR\\.Enumerator_CollectionChanged","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/VisualCollection.cs","lineNumber":920,"sourceCode":"\n                if (_version == _collection.Version)\n                {\n                    if ((_index > -2) && (_index < (_collection.InternalCount - 1)))\n                    {\n                        _index++;\n                        _currentElement = _collection[_index];\n                        return true;\n                    }\n                    else\n                    {\n                        _currentElement = null;\n                        _index = -2; // -2 <=> reached the end.\n                        return false;\n                    }\n                }\n                else\n                {\n                    throw new InvalidOperationException(SR.Enumerator_CollectionChanged);\n                }\n            }\n\n            /// <summary>\n            /// Gets the current Visual.\n            /// </summary>\n            object IEnumerator.Current\n            {\n                get\n                {\n                    return this.Current;\n                }\n            }\n\n            /// <summary>\n            /// Gets the current Visual.\n            /// </summary>\n            public Visual Current","sourceCodeStart":902,"sourceCodeEnd":938,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/VisualCollection.cs#L902-L938","documentation":"The VisualCollection Enumerator's MoveNext throws InvalidOperationException when the collection's version changed after the enumerator was created (a visual was added, removed, or moved during enumeration). WPF enumerators detect structural modification via a version stamp and fail fast instead of skipping or repeating items.","triggerScenarios":"Enumerating a VisualCollection (foreach or manual GetEnumerator) while another thread or re-entrant code calls Add/Insert/Remove/Clear/Move on the same collection, invalidating the version.","commonSituations":"Modifying children inside a loop over visual children; layout/arrange code mutating the tree while a caller iterates; enumerating from a non-UI thread while UI thread updates; iterating and calling RemoveChild in the same pass.","solutions":["Snapshot the children before enumerating: iterate over a copied array (e.g. foreach (Visual v in visualCollection.ToArray()))","Collect items to remove in one pass, then remove them after the loop","Do all collection mutations and enumerations on the UI thread and never re-entrantly inside layout callbacks","Use Count + indexer loop guarded appropriately instead of the enumerator"],"exampleFix":"// before\nforeach (Visual v in visualCollection)\n    if (ShouldRemove(v)) visualCollection.Remove(v); // throws\n// after\nvar toRemove = visualCollection.OfType<Visual>().Where(ShouldRemove).ToList();\nforeach (var v in toRemove) visualCollection.Remove(v);","handlingStrategy":"try-catch","validationCode":"int v0 = collection.Version; // detect modification yourself around the loop","typeGuard":null,"tryCatchPattern":"try { foreach (Visual v in collection) { Process(v); } }\ncatch (InvalidOperationException ex) when (ex.Message == SR.Enumerator_CollectionChanged || ex.Message.Contains(\"collection\")) { // re-enumerate over a snapshot }","preventionTips":["Iterate over a snapshot: collection.ToArray() or ToList()","Never mutate VisualCollection inside foreach; batch removals after the loop","Keep all visual-tree mutations on the UI thread","Avoid mutating children inside layout/measure/arrange callbacks"],"tags":["wpf","enumerator","collection-modified","invalid-operation"],"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"}