dotnet/wpf · error · InvalidOperationException
SR.EnumeratorVersionChanged
Error message
SR.EnumeratorVersionChanged
What it means
The view's enumerator (the IEnumerator returned for a CollectionView) throws InvalidOperationException (SR.EnumeratorVersionChanged) when MoveNext (or Reset) is called after the view was refreshed — the view's Timestamp changed, invalidating outstanding enumerators. This prevents enumerating over a view whose contents were rebuilt mid-iteration.
Solutions
- Materialize the view into a snapshot (ToList/ToArray) before long-running or re-entrant iteration
- Do not Refresh the view while an enumerator over it is still active
- Re-obtain the enumerator after each Refresh instead of caching it
- Use a using (DeferRefresh) around operations if the enumeration must complete first (then refresh after)
Example fix
// before
var e = view.GetEnumerator();
view.Refresh();
e.MoveNext(); // InvalidOperationException: version changed
// after
var snapshot = view.Cast<object>().ToList();
view.Refresh();
foreach (var item in snapshot) { } Defensive patterns
Strategy: validation
Validate before calling
var snapshot = view.Cast<object>().ToArray(); // stable across Refresh
foreach (var item in snapshot) { /* safe */ } Try / catch
try { e.MoveNext(); }
catch (InvalidOperationException) { e = view.GetEnumerator(); /* retry with fresh enumerator */ } Prevention
- Never cache enumerators of a CollectionView across Refresh calls
- Snapshot before async or re-entrant work
- Avoid mutating Filter/SortDescriptions while enumerating the view
When it happens
Trigger: Holding an IEnumerator from a CollectionView (e.g. from foreach with a yield, or stored enumerator) across a call to Refresh(), Filter/SortDescriptions change, or any RefreshInternal; the next MoveNext detects the timestamp mismatch.
Common situations: Async code iterating a view while the UI refreshes it; storing enumerators in fields; modifying Filter inside a loop that is enumerating the same view; LINQ deferred evaluation over a view that gets refreshed between iterations.
Related errors
- InvalidOperationException
- SR.Enumerator_CollectionChanged
- SR.Enumerator_CollectionChanged
- SR.Enumerator_CollectionChanged
- SR.Enumerator_CollectionChanged
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/8a3336195c7308db.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Data/CollectionView.cs:1630
#region Internal Types
internal class PlaceholderAwareEnumerator : IEnumerator
{
private enum Position { BeforePlaceholder, OnPlaceholder, OnNewItem, AfterPlaceholder}
public PlaceholderAwareEnumerator(CollectionView collectionView, IEnumerator baseEnumerator, NewItemPlaceholderPosition placeholderPosition, object newItem)
{
_collectionView = collectionView;
_timestamp = collectionView.Timestamp;
_baseEnumerator = baseEnumerator;
_placeholderPosition = placeholderPosition;
_newItem = newItem;
}
public bool MoveNext()
{
if (_timestamp != _collectionView.Timestamp)
throw new InvalidOperationException(SR.EnumeratorVersionChanged);
switch (_position)
{
case Position.BeforePlaceholder:
// AtBeginning - move to the placeholder
if (_placeholderPosition == NewItemPlaceholderPosition.AtBeginning)
{
_position = Position.OnPlaceholder;
}
// None or AtEnd - advance base, skipping the new item
else if (_baseEnumerator.MoveNext() &&
(_newItem == NoNewItem || _baseEnumerator.Current != _newItem
|| _baseEnumerator.MoveNext()))
{
}
// if base has reached the end, move to new item or placeholder
else if (_newItem != NoNewItem)
{View on GitHub (pinned to 81131a70a4)