AvaloniaUI/Avalonia · error · InvalidOperationException

Enumeration was not started. | Enumeration has ended.

Error message

Enumeration was not started. | Enumeration has ended.

What it means

InvalidOperationException from the PooledStack enumerator's Current getter via ThrowEnumerationNotStartedOrEnded, which selects the message by index: `-2` (before first MoveNext) yields "Enumeration was not started.", `-1` (after MoveNext returned false) yields "Enumeration has ended.". It prevents reading Current outside a valid enumeration position.

Source

Thrown at src/Avalonia.Base/Collections/Pooled/PooledStack.cs:682

                else
                    _currentElement = default;
                return retval;
            }

            public T Current
            {
                get
                {
                    if (_index < 0)
                        ThrowEnumerationNotStartedOrEnded();
                    return _currentElement!;
                }
            }

            private void ThrowEnumerationNotStartedOrEnded()
            {
                Debug.Assert(_index == -1 || _index == -2);
                throw new InvalidOperationException(_index == -2 ? "Enumeration was not started." : "Enumeration has ended.");
            }

            object? IEnumerator.Current
            {
                get { return Current; }
            }

            void IEnumerator.Reset()
            {
                if (_version != _stack._version)
                    throw new InvalidOperationException("Collection was modified during enumeration.");
                _index = -2;
                _currentElement = default;
            }
        }
    }
}

View on GitHub (pinned to 11c5427268)

Solutions

  1. Always call MoveNext() and check its return before reading Current.
  2. Prefer `foreach` which guarantees correct MoveNext/Current ordering.
  3. Discard an exhausted enumerator and obtain a fresh one rather than reading Current again.

Example fix

// before
using var e = stack.GetEnumerator();
var first = e.Current; // not started

// after
using var e = stack.GetEnumerator();
if (e.MoveNext())
    var first = e.Current;
Defensive patterns

Strategy: validation

Validate before calling

using var e = stack.GetEnumerator();
while (e.MoveNext())
    Use(e.Current); // Current only read after MoveNext==true

Prevention

When it happens

Trigger: Reading `enumerator.Current` before calling MoveNext, or after MoveNext has already returned false. Equivalent to the BCL enumerator rule enforced on Stack<T>.Enumerator.

Common situations: Calling Current on a freshly obtained enumerator without first calling MoveNext; reusing an exhausted enumerator; LINQ-style manual enumeration that skips the MoveNext gate.

Related errors


AI-assisted analysis of AvaloniaUI/Avalonia@11c5427268 (2026-08-13). Data as JSON: /api/errors/40b6a3fc9b53d62c. Report an issue: GitHub.