{"record":{"id":"b06af3e34f162a68","repo":"dotnet/wpf","slug":"sr-enumerator-collectionchanged-visual3dcollection","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/Media3D/Visual3DCollection.cs","lineNumber":618,"sourceCode":"                Debug.Assert(list != null, \"list may not be null.\");\n\n                _list = list;\n                _index = -1;\n                _version = _list._version;\n            }\n\n            #endregion Constructors\n\n            #region Public Methods\n\n            /// <summary>\n            ///     Advances the enumerator to the next IElement of the collection.\n            /// </summary>\n            public bool MoveNext()\n            {\n                if (_list._version != _version)\n                {\n                    throw new InvalidOperationException(SR.Enumerator_CollectionChanged);\n                }\n\n                int count = _list.Count;\n\n                if (_index < count)\n                {\n                    _index++;\n                }\n\n                return _index < count;\n            }\n\n            /// <summary>\n            ///     Resets the enumerator to its initial position.\n            /// </summary>\n            public void Reset()\n            {\n                if (_list._version != _version)","sourceCodeStart":600,"sourceCodeEnd":636,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media3D/Visual3DCollection.cs#L600-L636","documentation":"Visual3DCollection's enumerator detects that the underlying collection was modified (its _version changed) after the enumerator was created, and throws InvalidOperationException to prevent undefined iteration behavior. This is the standard fail-fast pattern for WPF collections whose enumerators do not tolerate mutation.","triggerScenarios":"Calling MoveNext() on a Visual3DCollection enumerator after any Add/Remove/Clear/Insert or other structural change to the collection since enumeration started.","commonSituations":"Iterating a Viewport3D's Children (Visual3DCollection) in a foreach while adding or removing Visual3D children on the same pass, often inside render/update code or a Loaded handler; or a background thread mutating the collection while the UI thread enumerates.","solutions":["Snapshot the collection before iterating: iterate over a copied array (e.g. visual3DCollection.ToArray()) when mutation is expected.","Restructure the loop so Add/Remove happens after enumeration completes, e.g. collect items to remove first, then apply.","Marshal all mutations to the UI thread and avoid mutating while any active enumerator exists; use index-based for loops iterating backwards when removing."],"exampleFix":"// before\nforeach (var v in viewport3D.Children)\n{\n    if (ShouldRemove(v)) viewport3D.Children.Remove(v); // throws\n}\n// after\nforeach (var v in viewport3D.Children.ToArray())\n{\n    if (ShouldRemove(v)) viewport3D.Children.Remove(v);\n}","handlingStrategy":"validation","validationCode":"// enumerate a snapshot if mutation is possible\nvar snapshot = children.ToArray(); // System.Linq","typeGuard":"static bool CanSafelyEnumerate(System.Windows.Media.Media3D.Visual3DCollection c) => c != null; // enumerators do not expose version state, so guard by snapshotting","tryCatchPattern":"try { foreach (var v in children) { /* ... */ } }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"changed\") || ex.Message.Contains(\"Enumerator\"))\n{ foreach (var v in children.ToArray()) { /* retry on snapshot */ } }","preventionTips":["Never call Add/Remove/Clear on a Visual3DCollection inside its own foreach; collect changes and apply afterwards.","Snapshot with ToArray() when mutation is expected during iteration.","Keep all Visual3D collection mutations on the UI thread."],"tags":["wpf","enumerator","collection-modified","concurrency"],"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"}