dotnet/wpf · error · InvalidOperationException

SR.Enumerator_CollectionChanged

Error message

SR.Enumerator_CollectionChanged

What it means

The collection's enumerator throws InvalidOperationException (SR.Enumerator_CollectionChanged) in MoveNext when the underlying collection's version stamp no longer matches the version captured when the enumerator was created. WPF collections fail fast on structural change during enumeration rather than risk undefined behavior.

Solutions

  1. Snapshot the items before mutating: foreach (var d in collection.ToList()) { ... }
  2. Iterate backwards over the indexed collection with a for loop when removing
  3. Defer mutations until after enumeration completes

Example fix

// before
foreach (var d in collection) if (IsUnwanted(d)) collection.Remove(d);
// after
foreach (var d in collection.ToList()) if (IsUnwanted(d)) collection.Remove(d);
Defensive patterns

Strategy: try-catch

Try / catch

try
{
    foreach (var d in collection) { /* ... */ }
}
catch (InvalidOperationException ex) when (ex.Message.Contains("changed"))
{
    // re-enumerate over a snapshot: collection.ToList()
}

Prevention

When it happens

Trigger: Calling Add/Insert/Remove/Clear on the TextDecorationCollection inside a foreach over it, then continuing to call MoveNext.

Common situations: Modifying decorations in a loop (e.g. removing all underline decorations while iterating); event handlers that mutate the collection during enumeration; UI code reacting to property changes triggered mid-loop.

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/99556eb04bfe4006. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Generated/TextDecorationCollection.cs:788

            {
                _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)