stride3d/stride · error · InvalidOperationException
The collection was modified after the enumerator was…
Error message
The collection was modified after the enumerator was created.
What it means
MultiValueSortedDictionary's enumerator tracks whether the underlying dictionary was modified (via the _IsModified flag). MoveNext throws InvalidOperationException('The collection was modified after the enumerator was created.') when any Add/Remove/Clear happened after enumeration started; this enumerator does not support concurrent modification.
Solutions
- Collect the keys/entries to change into a temporary list inside the loop, then apply changes after enumeration
- Remove by copying keys first: foreach (var k in keysSnapshot) dict.Remove(k)
- Synchronize with a lock so no mutation occurs while enumerating, or take a snapshot (ToArray/ToList) before modifying
- Restart the enumeration after the modification if order matters
Example fix
// before
foreach (var kv in dict)
if (predicate(kv.Value)) dict.Remove(kv.Key); // throws
// after
foreach (var key in dict.Keys.ToList())
if (predicate(dict[key])) dict.Remove(key); Defensive patterns
Strategy: try-catch
Try / catch
try { foreach (var kv in dict) { /* ... */ } } catch (InvalidOperationException) { /* re-enumerate after mutation */ } Prevention
- Never mutate a dictionary during foreach; batch changes into a list and apply after
- Take a ToList snapshot of keys/entries before modifying
- Use locking for cross-thread access
- Obtain a fresh enumerator after modifications
When it happens
Trigger: Enumerating the dictionary (foreach) and calling Add, Insert, Remove, or Clear on the same dictionary inside or between MoveNext calls; modifying the dictionary from another thread while an enumeration is in progress.
Common situations: Removing entries in a foreach loop over the dictionary; building/removing entries while iterating for logging or serialization; shared dictionary mutated by another thread during UI-driven enumeration.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- This operation is not supported by the source tracker.
- An item with the same key has already been added.
- The given key was not present in the dictionary.
- Internal error, the collection has already muted to a…
- InvalidOperationException
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/ab3a4c3b79fc5ca2.
Report an issue: GitHub.
Appendix: source
Thrown at sources/core/Stride.Core/Collections/MultiValueSortedDictionary.cs:880
}
/// <summary>
/// Advances the enumerator to the next element of the KoderHack.MultiValueSortedDictionary<TKey,TValue>.
/// </summary>
/// <returns>
/// true if the enumerator was successfully advanced to the next element; false
/// if the enumerator has passed the end of the collection.
/// </returns>
/// <exception cref="System.InvalidOperationException">
/// The collection was modified after the enumerator was created.
/// </exception>
public bool MoveNext()
{
//if (_Disposed)
// throw new InvalidOperationException("The enumerator has already been disposed.");
if (_Dictionary._IsModified)
throw new InvalidOperationException("The collection was modified after the enumerator was created.");
if (!_Valid) return false;
var key = default(TKey);
var value = default(TValue);
if (_Enumerator2 == null)
{
if (_Enumerator1.MoveNext())
{
if (_Enumerator1.Current.Value != null)
_Enumerator2 = _Enumerator1.Current.Value.GetEnumerator();
}
else
_Valid = false;
}
if (!_Valid) return false;
key = _Enumerator1.Current.Key;View on GitHub (pinned to 96fad776d2)