dotnet/wpf · error · InvalidOperationException
SR.Enumerator_ReachedEnd
Error message
SR.Enumerator_ReachedEnd
What it means
GeneralTransformCollection.Enumerator.Current throws this InvalidOperationException when the enumerator has already walked past the end of the collection (internal _index == -2, set when MoveNext() returned false). Per the IEnumerator.Current contract, reading Current after exhaustion must throw rather than return the last item or default.
Solutions
- Read Current only inside the loop guarded by a true result from MoveNext().
- Keep a local variable holding the last valid item instead of re-reading Current after iteration finishes.
- Replace manual enumerator code with foreach, which never reads Current after the end.
- If a re-read is needed, call Reset() then MoveNext() to reposition before accessing Current.
Example fix
// before
while (e.MoveNext()) { }
var last = e.Current; // throws: past the end
// after
object last = null;
while (e.MoveNext()) { last = e.Current; } Defensive patterns
Strategy: validation
Validate before calling
// keep the last valid item while iterating
T last = default;
while (enumerator.MoveNext()) { last = enumerator.Current; } Type guard
static bool TryGetCurrent<T>(this IEnumerator<T> e, out T current) { bool ok = e.MoveNext(); current = ok ? e.Current : default; return ok; } Try / catch
try { var v = enumerator.Current; }
catch (InvalidOperationException) { /* enumerator exhausted: capture items during iteration instead */ } Prevention
- Never read Current after MoveNext() returned false.
- Capture the desired item in a local variable inside the loop.
- Use foreach, which guarantees Current is only read on valid positions.
- If the last item is needed, track it during iteration rather than re-reading.
When it happens
Trigger: Accessing Current after MoveNext() has returned false (collection fully consumed), then calling MoveNext() again (which keeps _index at -2) and reading Current, or reading Current repeatedly after the loop ended.
Common situations: Manual while(e.MoveNext()) loops that read e.Current after the loop body; code that stores the enumerator and reads Current later assuming it keeps the last value; loops with a trailing Current read outside the MoveNext()==true check.
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
- SR.Enumerator_NotStarted
- SR.Enumerator_CollectionChanged
- SR.Enumerator_NotStarted
- SR.Enumerator_NotStarted
- SR.Enumerator_NotStarted
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/f0bf6c679165f381.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/GeneralTransformCollection.cs:854
/// off the end of the list. However, the IEnumerable.Current
/// contract requires that we throw exceptions
/// </summary>
public GeneralTransform Current
{
get
{
if (_index > -1)
{
return _current;
}
else if (_index == -1)
{
throw new InvalidOperationException(SR.Enumerator_NotStarted);
}
else
{
Debug.Assert(_index == -2, "expected -2, got " + _index + "\n");
throw new InvalidOperationException(SR.Enumerator_ReachedEnd);
}
}
}
#endregion
#region Data
private GeneralTransform _current;
private GeneralTransformCollection _list;
private uint _version;
private int _index;
#endregion
}
#endregion
#region Constructors
//------------------------------------------------------View on GitHub (pinned to 81131a70a4)