dotnet/wpf · error · InvalidOperationException

SR.Enumerator_CollectionChanged

Error message

SR.Enumerator_CollectionChanged

What it means

This SR resource backs an InvalidOperationException thrown by the generated collection's enumerator (CollectionHelper.cs) when MoveNext detects the collection's version changed after the enumerator was created. The enumerator is invalidated to prevent skipping or duplicating elements.

Solutions

  1. Materialize the collection (ToList()) before mutating, or iterate over a snapshot
  2. Collect pending changes and apply them after the loop
  3. Avoid mutating the collection inside foreach; use a for loop over indices from the end when removing
  4. Catch InvalidOperationException at the iteration boundary if concurrent mutation is possible and retry with a fresh enumerator

Example fix

// before
foreach (var item in collection) if (pred(item)) collection.Remove(item); // throws
// after
foreach (var item in collection.ToList()) if (pred(item)) collection.Remove(item);
Defensive patterns

Strategy: try-catch

Try / catch

try { foreach (var i in collection) { /* no mutation */ } }
catch (InvalidOperationException) { iterateSnapshot(collection.ToList()); }

Prevention

When it happens

Trigger: Calling MoveNext() on an enumerator obtained before any Add/Insert/Remove/Clear (or other version bump) on the same generated collection.

Common situations: Modifying a collection while enumerating it (e.g. removing items inside a foreach); enumerating on one thread while another mutates the collection; storing enumerators across update passes.

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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/WpfGfx/codegen/mcg/helpers/CollectionHelper.cs:716

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