{"record":{"id":"4a6268d57d8a1193","repo":"dotnet/wpf","slug":"sr-enumerator-collectionchanged-texteffectcollection","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/Generated/TextEffectCollection.cs","lineNumber":797,"sourceCode":"            {\n                _list.ReadPreamble();\n\n                if (_version == _list._version)\n                {\n                    if (_index > -2 && _index < _list._collection.Count - 1)\n                    {\n                        _current = _list._collection[++_index];\n                        return true;\n                    }\n                    else\n                    {\n                        _index = -2; // -2 indicates \"past the end\"\n                        return false;\n                    }\n                }\n                else\n                {\n                    throw new InvalidOperationException(SR.Enumerator_CollectionChanged);\n                }\n            }\n\n            /// <summary>\n            /// Sets the enumerator to its initial position, which is before the\n            /// first element in the collection.\n            /// </summary>\n            public void Reset()\n            {\n                _list.ReadPreamble();\n\n                if (_version == _list._version)\n                {\n                    _index = -1;\n                }\n                else\n                {\n                    throw new InvalidOperationException(SR.Enumerator_CollectionChanged);","sourceCodeStart":779,"sourceCodeEnd":815,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/TextEffectCollection.cs#L779-L815","documentation":"TextEffectCollection's enumerator throws InvalidOperationException(Enumerator_CollectionChanged) in MoveNext when the collection's version changed after the enumerator was created. WPF collections use a version stamp to detect modification during enumeration and fail fast instead of returning inconsistent data.","triggerScenarios":"Calling MoveNext after the collection was modified (Add/Remove/Insert/Clear) since GetEnumerator was called — e.g. adding an item inside a foreach over the collection.","commonSituations":"Removing an effect while iterating; modifying the collection from a PropertyChanged or render callback during enumeration; cross-thread mutation.","solutions":["Collect the items into a snapshot array/List first, then iterate and modify safely","Move collection mutations outside the foreach loop","Use a for loop over an index against a snapshot copy"],"exampleFix":"// before\nforeach (var e in collection)\n{\n    if (e.ShouldRemove) collection.Remove(e); // throws\n}\n// after\nforeach (var e in collection.ToArray())\n{\n    if (e.ShouldRemove) collection.Remove(e);\n}","handlingStrategy":"try-catch","validationCode":"// snapshot before iterating\nvar snapshot = collection.ToArray();","typeGuard":null,"tryCatchPattern":"try { foreach (var e in snapshot) { /* mutate collection here safely */ } }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"changed\"))\n{ /* restart enumeration with a fresh snapshot */ }","preventionTips":["Never Add/Remove/Clear during foreach over the same collection","Iterate over collection.ToArray() when mutation is needed","Avoid mutating the collection from event handlers fired during enumeration","If multithreaded, guard mutations with a lock"],"tags":["wpf","enumeration","invalidoperationexception","collection-modified"],"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"}