dotnet/wpf · error · InvalidOperationException

InvalidOperationException

Error message

InvalidOperationException

What it means

LeafEnumerator detects that its parent group was modified (version bump) after enumeration started, and MoveNext throws a bare InvalidOperationException. Enumerators over grouped collection views are invalidated by structural changes to the group.

Solutions

  1. Snapshot the leaves (e.g. ToList()) before mutating or holding the enumerator
  2. Make collection changes after enumeration completes, or re-enumerate after changes
  3. Avoid mutating the grouped source from within enumeration; defer via Dispatcher.BeginInvoke

Example fix

// before
foreach (var leaf in group.Leaves) items.Remove(leaf);
// after
var snapshot = group.Leaves.Cast<object>().ToList();
foreach (var leaf in snapshot) items.Remove(leaf);
Defensive patterns

Strategy: try-catch

Validate before calling

if (group._version != capturedVersion) { enumerator = group.GetLeafEnumerator(); }

Try / catch

try { while (e.MoveNext()) { /* ... */ } } catch (InvalidOperationException) { e = group.GetLeafEnumerator(); /* re-enumerate */ }

Prevention

When it happens

Trigger: Continuing a foreach over a group's leaves after adding/removing/moving items in the grouped source (or regrouping) mid-enumeration.

Common situations: Modifying the ObservableCollection in a CollectionChanged handler while UI enumerates groups; enumerating on a background thread while UI thread refreshes the view; deferred LINQ evaluation over stale group enumerators.

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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/Data/CollectionViewGroupInternal.cs:776

            }

            void IEnumerator.Reset()
            {
                DoReset();
            }

            private void DoReset()
            {
                _version = _group._version;
                _index = -1;
                _subEnum = null;
            }

            bool IEnumerator.MoveNext()
            {
                // check for invalidated enumerator
                if (_group._version != _version)
                    throw new InvalidOperationException();

                // move forward to the next leaf
                while (_subEnum == null || !_subEnum.MoveNext())
                {
                    // done with the current top-level item.  Move to the next one.
                    ++_index;
                    if (_index >= _group.Items.Count)
                        return false;

                    CollectionViewGroupInternal subgroup = _group.Items[_index] as CollectionViewGroupInternal;
                    if (subgroup == null)
                    {
                        // current item is a leaf - it's the new Current
                        _current = _group.Items[_index];
                        _subEnum = null;
                        return true;
                    }
                    else

View on GitHub (pinned to 81131a70a4)