dotnet/wpf · error · InvalidOperationException

SR.Enumerator_CollectionChanged

Error message

SR.Enumerator_CollectionChanged

What it means

The PointCollection enumerator is version-checked: it throws InvalidOperationException(Enumerator_CollectionChanged) from MoveNext when the collection's version counter no longer matches the version captured when the enumerator was created. WPF invalidates outstanding enumerators on any modification (Add, Remove, Clear, indexer set) so iteration never observes a collection mutated mid-enumeration.

Solutions

  1. Snapshot first: foreach over a copied array (points.ToArray()) and mutate the original.
  2. Collect items to remove into a separate list, then apply the changes after the loop.
  3. Use a for loop over the index (counting backwards for removals) instead of foreach.
  4. Synchronize threads so no mutation happens while enumerating.

Example fix

// before
foreach (Point p in points)
    if (p.X < 0) points.Remove(p); // InvalidOperationException
// after
foreach (Point p in points.ToArray())
    if (p.X < 0) points.Remove(p);
Defensive patterns

Strategy: validation

Validate before calling

long versionBefore = points.GetType().GetProperty("Version", BindingFlags.NonPublic|BindingFlags.Instance)?.GetValue(points) as long? ?? 0; // or simply: iterate over a snapshot

Type guard

null

Try / catch

try { foreach (Point p in points) { /* ... */ } }
catch (InvalidOperationException ex) when (ex.Message.Contains("CollectionChanged")) { /* restart iteration over a fresh snapshot */ }

Prevention

When it happens

Trigger: Calling MoveNext on an Enumerator after any structural change to the underlying PointCollection — e.g. points.Add(...) or points.RemoveAt(i) inside a foreach over points, or on another thread.

Common situations: Modifying a collection inside foreach and removing items; deferred logic that mutates the collection between obtaining the enumerator and iterating; UI/data updates on a different thread while an enumeration is in progress.

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/8d4293fc9e2a52c7. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/PointCollection.cs:817

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