dotnet/wpf · error · InvalidOperationException
SR.Enumerator_NotStarted
Error message
SR.Enumerator_NotStarted
What it means
This InvalidOperationException is thrown by a strongly-typed collection enumerator generated by CollectionHelper (in the WPF mcg codegen tool) when MoveNext() has never been called before accessing Current. The enumerator uses _index == -1 to mean 'before first MoveNext', -2 for 'past the end', and >= 0 for a valid position. The library enforces the IEnumerator contract that Current is only valid between a successful MoveNext() and the next one.
Solutions
- Call MoveNext() and check its return value before accessing Current, or rewrite as a foreach loop which does this automatically.
- If you need to inspect the first element, do `if (e.MoveNext()) { var first = e.Current; }`.
- Reset the enumerator (or get a new one) if you previously exhausted it and want to iterate again.
Example fix
// before
var e = collection.GetEnumerator();
var item = e.Current; // throws: not started
// after
var e = collection.GetEnumerator();
while (e.MoveNext())
{
var item = e.Current;
} Defensive patterns
Strategy: try-catch
Validate before calling
if (list == null || list.Count == 0) return; // nothing to read var e = list.GetEnumerator(); if (!e.MoveNext()) return; // guard before reading Current
Type guard
static bool TryFirst<T>(IEnumerator<T> e, out T value) { value = default; if (e == null) return false; if (!e.MoveNext()) return false; value = e.Current; return true; } Try / catch
try { var item = enumerator.Current; }
catch (InvalidOperationException ex) when (ex.Message.Contains("not started") || ex.Message.Contains("NotStarted")) { /* call MoveNext() first */ } Prevention
- Prefer foreach over manual enumerator loops.
- Never read Current before a successful MoveNext().
- Never reuse an enumerator across iterations without Reset().
When it happens
Trigger: Reading the Current property of the generated collection's enumerator before calling MoveNext() at least once, e.g. `var e = list.GetEnumerator(); var x = e.Current;`.
Common situations: Hand-rolled iteration loops that skip the initial MoveNext; code copied from foreach into a manual loop; using Current to 'peek' at the first element; refactoring that moved the MoveNext call after the first Current access.
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_ReachedEnd
- 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/fe31c5b00bc1f4bc.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/WpfGfx/codegen/mcg/helpers/CollectionHelper.cs:768
/// <summary>
/// Current element
///
/// The behavior of IEnumerable<T>.Current is undefined
/// before the first MoveNext and after we have walked
/// off the end of the list. However, the IEnumerable.Current
/// contract requires that we throw exceptions
/// </summary>
public [[type]] 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 [[type]] _current;
private [[resource.Name]] _list;
private uint _version;
private int _index;
#endregion
}View on GitHub (pinned to 81131a70a4)