dotnet/wpf · error · InvalidOperationException

SR.Enumerator_CollectionChanged

Error message

SR.Enumerator_CollectionChanged

What it means

TextEffectCollection's enumerator throws InvalidOperationException(Enumerator_CollectionChanged) in MoveNext when the collection's version changed after the enumerator was created. WPF collections use a version stamp to detect modification during enumeration and fail fast instead of returning inconsistent data.

Solutions

  1. Collect the items into a snapshot array/List first, then iterate and modify safely
  2. Move collection mutations outside the foreach loop
  3. Use a for loop over an index against a snapshot copy

Example fix

// before
foreach (var e in collection)
{
    if (e.ShouldRemove) collection.Remove(e); // throws
}
// after
foreach (var e in collection.ToArray())
{
    if (e.ShouldRemove) collection.Remove(e);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// snapshot before iterating
var snapshot = collection.ToArray();

Try / catch

try { foreach (var e in snapshot) { /* mutate collection here safely */ } }
catch (InvalidOperationException ex) when (ex.Message.Contains("changed"))
{ /* restart enumeration with a fresh snapshot */ }

Prevention

When it happens

Trigger: Calling MoveNext after the collection was modified (Add/Remove/Insert/Clear) since GetEnumerator was called — e.g. adding an item inside a foreach over the collection.

Common situations: Removing an effect while iterating; modifying the collection from a PropertyChanged or render callback during enumeration; cross-thread mutation.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/4a6268d57d8a1193. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/TextEffectCollection.cs:797

            {
                _list.ReadPreamble();

                if (_version == _list._version)
                {
                    if (_index > -2 && _index < _list._collection.Count - 1)
                    {
                        _current = _list._collection[++_index];
                        return true;
                    }
                    else
                    {
                        _index = -2; // -2 indicates "past the end"
                        return false;
                    }
                }
                else
                {
                    throw new InvalidOperationException(SR.Enumerator_CollectionChanged);
                }
            }

            /// <summary>
            /// Sets the enumerator to its initial position, which is before the
            /// first element in the collection.
            /// </summary>
            public void Reset()
            {
                _list.ReadPreamble();

                if (_version == _list._version)
                {
                    _index = -1;
                }
                else
                {
                    throw new InvalidOperationException(SR.Enumerator_CollectionChanged);

View on GitHub (pinned to 81131a70a4)