{"record":{"id":"dd217fd4420ef7cc","repo":"egametang/ET","slug":"invalidoperation-enumfailedversion","errorCode":null,"errorMessage":"InvalidOperation_EnumFailedVersion","messagePattern":"InvalidOperation_EnumFailedVersion","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"Packages/cn.etetet.core/Scripts/Core/Share/Collection/SortedSet.cs","lineNumber":2059,"sourceCode":"                    else if (next == null || !_tree.IsWithinRange(next.Item))\n                    {\n                        node = other;\n                    }\n                    else\n                    {\n                        node = next;\n                    }\n                }\n            }\n\n            public bool MoveNext()\n            {\n                // Make sure that the underlying subset has not been changed since\n                _tree.VersionCheck();\n\n                if (_version != _tree.version)\n                {\n                    throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion);\n                }\n\n                if (_stack.Count == 0)\n                {\n                    _current = null;\n                    return false;\n                }\n\n                _current = _stack.Pop();\n                Node node = (_reverse ? _current.Left : _current.Right);\n                Node next, other;\n                while (node != null)\n                {\n                    next = (_reverse ? node.Right : node.Left);\n                    other = (_reverse ? node.Left : node.Right);\n                    if (_tree.IsWithinRange(node.Item))\n                    {\n                        _stack.Push(node);","sourceCodeStart":2041,"sourceCodeEnd":2077,"githubUrl":"https://github.com/egametang/ET/blob/5cab01f7a8bee5f49f4781eebe9e2b1c6d7ebe0f/Packages/cn.etetet.core/Scripts/Core/Share/Collection/SortedSet.cs#L2041-L2077","documentation":"Thrown by SortedSet Enumerator.MoveNext when the set's version no longer matches the enumerator's captured _version. SortedSet bumps its version on every structural modification (Add/Remove/Clear), and the enumerator detects the drift to fail fast rather than yield inconsistent data. Surfaces as InvalidOperationException (InvalidOperation_EnumFailedVersion).","triggerScenarios":"Modifying the SortedSet (Add/Remove/Clear/GetViewBetween that mutates) inside a foreach over the same set; holding an enumerator across a later mutation then calling MoveNext; concurrent access from another fiber/thread.","commonSituations":"foreach (var x in set) set.Remove(x); the classic mutate-while-enumerating pattern; nested operations that re-enter the set; multi-threaded ET fiber access without synchronization.","solutions":["Collect items to remove/add into a separate list during enumeration, then apply the mutations after the loop.","Use set.RemoveWhere(predicate) for filter-style mutations, which handles versioning internally.","Snapshot with set.ToArray()/ToList() before mutating, or lock/synchronize cross-fiber access."],"exampleFix":"// before\nforeach (var x in set) { if (pred(x)) set.Remove(x); } // throws\n\n// after\nset.RemoveWhere(pred);","handlingStrategy":"validation","validationCode":"// Buffer mutations during enumeration.\nList<T> toRemove = new List<T>();\nforeach (var x in set) if (pred(x)) toRemove.Add(x);\nforeach (var x in toRemove) set.Remove(x);","typeGuard":null,"tryCatchPattern":"try { foreach (var x in set) { /* ... */ } } catch (InvalidOperationException ex) when (ex.Message.Contains(\"EnumFailedVersion\")) { /* snapshot with ToArray and retry */ }","preventionTips":["Use RemoveWhere for filter mutations.","Snapshot ToArray/ToList before mutating during iteration.","Synchronize cross-fiber access to shared SortedSets."],"tags":["sortedset","enumeration","concurrent-modification","collection","csharp"],"backgroundTag":null,"analyzedSha":"5cab01f7a8bee5f49f4781eebe9e2b1c6d7ebe0f","analyzedAt":"2026-08-13T21:10:40.377Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}