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

  1. Call MoveNext() and check its return value before accessing Current, or rewrite as a foreach loop which does this automatically.
  2. If you need to inspect the first element, do `if (e.MoveNext()) { var first = e.Current; }`.
  3. 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

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


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&lt;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)