stride3d/stride · error · InvalidOperationException
Collection was modified after the enumerator was…
Error message
Collection was modified after the enumerator was instantiated.
What it means
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.
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
Example fix
// before
foreach (var kv in sortedList.KeyValues)
if (kv.Value == null) sortedList.Remove(kv.Key); // throws
// after
foreach (var key in sortedList.Keys.ToArray())
if (sortedList[key] == null) sortedList.Remove(key); Defensive patterns
Strategy: try-catch
Try / catch
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); } Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- is not a supported mode.
- Cannot enable tracking when this instance is disposed
- The order of the Asset.Id property must be lower than the…
- Event handlers can't be added or removed after the…
- RoutingSerializer expected in the chain of serializers
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/690c3c59ee54a2fd.
Report an issue: GitHub.
Appendix: source
Thrown at sources/core/Stride.Core/Collections/SortedList.cs:832
int idx;
readonly int ver;
internal KeyEnumerator(SortedList<TKey, TValue> l)
{
this.l = l;
idx = NOT_STARTED;
ver = l.modificationCount;
}
public void Dispose()
{
idx = NOT_STARTED;
}
public bool MoveNext()
{
if (ver != l.modificationCount)
throw new InvalidOperationException("Collection was modified after the enumerator was instantiated.");
if (idx == NOT_STARTED)
idx = l.Count;
return idx != FINISHED && --idx != FINISHED;
}
public TKey Current
{
get
{
if (idx < 0)
throw new InvalidOperationException();
return l.KeyAt(l.Count - 1 - idx);
}
}
View on GitHub (pinned to 96fad776d2)