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
- Always call MoveNext() and gate on its boolean return before reading Current.
- Use foreach (or the public Enumerator) which never reads Current out of band.
- 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
- Always gate Current on MoveNext's return value.
- Prefer the public typed Enumerator / foreach over the boxed IEnumerator path.
- Discard exhausted enumerators rather than reading Current again.
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
- Collection was modified during enumeration.
- Collection was modified during enumeration.
- Enumeration was not started. | Enumeration has ended.
- Stack was empty.
- Enumeration has not started.
AI-assisted analysis of AvaloniaUI/Avalonia@11c5427268 (2026-08-13).
Data as JSON: /api/errors/1996af6c30baa178.
Report an issue: GitHub.