{"record":{"id":"690c3c59ee54a2fd","repo":"stride3d/stride","slug":"collection-was-modified-after-the-enumerator-was","errorCode":null,"errorMessage":"Collection was modified after the enumerator was instantiated.","messagePattern":"Collection was modified after the enumerator was instantiated\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/core/Stride.Core/Collections/SortedList.cs","lineNumber":832,"sourceCode":"        int idx;\n        readonly int ver;\n\n        internal KeyEnumerator(SortedList<TKey, TValue> l)\n        {\n            this.l = l;\n            idx = NOT_STARTED;\n            ver = l.modificationCount;\n        }\n\n        public void Dispose()\n        {\n            idx = NOT_STARTED;\n        }\n\n        public bool MoveNext()\n        {\n            if (ver != l.modificationCount)\n                throw new InvalidOperationException(\"Collection was modified after the enumerator was instantiated.\");\n\n            if (idx == NOT_STARTED)\n                idx = l.Count;\n\n            return idx != FINISHED && --idx != FINISHED;\n        }\n\n        public TKey Current\n        {\n            get\n            {\n                if (idx < 0)\n                    throw new InvalidOperationException();\n\n                return l.KeyAt(l.Count - 1 - idx);\n            }\n        }\n","sourceCodeStart":814,"sourceCodeEnd":850,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/core/Stride.Core/Collections/SortedList.cs#L814-L850","documentation":"SortedList's key-value enumerator tracks a version number captured at creation and compares it to the list's modificationCount on every MoveNext(). If the list was mutated (Add/Remove/Insert/Clear or indexer set) after the enumerator was created, iteration is aborted with InvalidOperationException because the underlying sort order may have shifted.","triggerScenarios":"Calling Add/Remove/RemoveAt/Insert/indexer-set/Clear on a SortedList between GetEnumerator() and a subsequent MoveNext() while enumerating keys and values together (the KeyValueCollection enumerator).","commonSituations":"Removing an item from a SortedList inside a foreach loop; a background thread mutating the collection while the UI thread iterates it; modifying the list inside a LINQ chain that is lazily evaluated.","solutions":["Snapshot the items first (e.g. ToArray()) and enumerate the snapshot while mutating the original","Restructure the loop to collect items to remove, then remove them after the loop ends","Use a for loop over indices descending (for i = Count-1; i >= 0; i--) when removing during iteration","Synchronize access with a lock if another thread mutates the list"],"exampleFix":"// before\nforeach (var kv in sortedList.KeyValues)\n    if (kv.Value == null) sortedList.Remove(kv.Key); // throws\n// after\nforeach (var key in sortedList.Keys.ToArray())\n    if (sortedList[key] == null) sortedList.Remove(key);","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { foreach (var kv in sortedList.KeyValues) Process(kv); } catch (InvalidOperationException ex) when (ex.Message.Contains(\"Collection was modified\")) { /* restart with a snapshot */ foreach (var kv in sortedList.KeyValues.ToArray()) Process(kv); }","preventionTips":["Snapshot the collection (ToArray/ToList) before iterating when mutation is possible","Never Add/Remove on the SortedList inside a foreach over it","Use lock-free immutable copies or locks in multithreaded access","Defer removals until after the loop completes"],"tags":["enumerator","invalidoperationexception","collection-modified","sortedlist"],"backgroundTag":"collection-was-modified-during-iteration","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}