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

  1. Collect the keys/entries to change into a temporary list inside the loop, then apply changes after enumeration
  2. Remove by copying keys first: foreach (var k in keysSnapshot) dict.Remove(k)
  3. Synchronize with a lock so no mutation occurs while enumerating, or take a snapshot (ToArray/ToList) before modifying
  4. 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

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


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&lt;TKey,TValue&gt;.
        /// </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)