AvaloniaUI/Avalonia · error · InvalidOperationException

Invalid enumerator state: enumeration cannot proceed.

Error message

Invalid enumerator state: enumeration cannot proceed.

What it means

Centralized ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen throws InvalidOperationException "Invalid enumerator state: enumeration cannot proceed." It is actively called from PooledList<T>.Enumerator's non-generic IEnumerator.Current getter when `_index == 0` (before any MoveNext) or `_index == _list._size + 1` (after exhaustion). It is PooledList's combined guard that Current is read only within a valid, in-progress enumeration.

Source

Thrown at src/Avalonia.Base/Collections/Pooled/ThrowHelper.cs:312

            throw new InvalidOperationException("Enumeration has ended.");
        }

        [DoesNotReturn]
        internal static void ThrowInvalidOperationException_EnumCurrent(int index)
        {
            throw GetInvalidOperationException_EnumCurrent(index);
        }

        [DoesNotReturn]
        internal static void ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion()
        {
            throw new InvalidOperationException("Collection was modified during enumeration.");
        }

        [DoesNotReturn]
        internal static void ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen()
        {
            throw new InvalidOperationException("Invalid enumerator state: enumeration cannot proceed.");
        }

        [DoesNotReturn]
        internal static void ThrowInvalidOperationException_InvalidOperation_NoValue()
        {
            throw new InvalidOperationException("No value provided.");
        }

        [DoesNotReturn]
        internal static void ThrowInvalidOperationException_ConcurrentOperationsNotSupported()
        {
            throw new InvalidOperationException("Concurrent operations are not supported.");
        }

        [DoesNotReturn]
        internal static void ThrowInvalidOperationException_HandleIsNotInitialized()
        {
            throw new InvalidOperationException("Handle is not initialized.");

View on GitHub (pinned to 11c5427268)

Solutions

  1. Always call MoveNext() and gate on its boolean return before reading Current.
  2. Use foreach (or the public Enumerator) which never reads Current out of band.
  3. Discard and recreate the enumerator rather than reading Current after exhaustion.

Example fix

// before
var e = ((IEnumerable)list).GetEnumerator();
var v = e.Current; // index 0 -> throws

// after
var e = ((IEnumerable)list).GetEnumerator();
if (e.MoveNext())
    var v = e.Current;
Defensive patterns

Strategy: validation

Validate before calling

var e = list.GetEnumerator();
while (e.MoveNext())
    Use(e.Current); // only valid while MoveNext returned true

Prevention

When it happens

Trigger: Reading `((IEnumerator)list).Current` before calling MoveNext, or after MoveNext has returned false, on a PooledList enumerator. The boxed/non-generic enumerator path is what hits this helper.

Common situations: Manual IEnumerator usage that skips MoveNext; LINQ/foreach desugaring edge cases; reusing an exhausted boxed enumerator; interop that drives IEnumerator.Current directly.

Related errors


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