dotnet/wpf · error · InvalidOperationException
SR.Enumerator_CollectionChanged
Error message
SR.Enumerator_CollectionChanged
What it means
The VisualCollection Enumerator's MoveNext throws InvalidOperationException when the collection's version changed after the enumerator was created (a visual was added, removed, or moved during enumeration). WPF enumerators detect structural modification via a version stamp and fail fast instead of skipping or repeating items.
Solutions
- Snapshot the children before enumerating: iterate over a copied array (e.g. foreach (Visual v in visualCollection.ToArray()))
- Collect items to remove in one pass, then remove them after the loop
- Do all collection mutations and enumerations on the UI thread and never re-entrantly inside layout callbacks
- Use Count + indexer loop guarded appropriately instead of the enumerator
Example fix
// before
foreach (Visual v in visualCollection)
if (ShouldRemove(v)) visualCollection.Remove(v); // throws
// after
var toRemove = visualCollection.OfType<Visual>().Where(ShouldRemove).ToList();
foreach (var v in toRemove) visualCollection.Remove(v); Defensive patterns
Strategy: try-catch
Validate before calling
int v0 = collection.Version; // detect modification yourself around the loop
Try / catch
try { foreach (Visual v in collection) { Process(v); } }
catch (InvalidOperationException ex) when (ex.Message == SR.Enumerator_CollectionChanged || ex.Message.Contains("collection")) { // re-enumerate over a snapshot } Prevention
- Iterate over a snapshot: collection.ToArray() or ToList()
- Never mutate VisualCollection inside foreach; batch removals after the loop
- Keep all visual-tree mutations on the UI thread
- Avoid mutating children inside layout/measure/arrange callbacks
When it happens
Trigger: Enumerating a VisualCollection (foreach or manual GetEnumerator) while another thread or re-entrant code calls Add/Insert/Remove/Clear/Move on the same collection, invalidating the version.
Common situations: Modifying children inside a loop over visual children; layout/arrange code mutating the tree while a caller iterates; enumerating from a non-UI thread while UI thread updates; iterating and calling RemoveChild in the same pass.
Related errors
- SR.EnumeratorCollectionDisposed
- InvalidOperationException
- InvalidOperationException
- SR.Enumerator_CollectionChanged
- SR.Enumerator_CollectionChanged
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/b237503a703b847d.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/VisualCollection.cs:920
if (_version == _collection.Version)
{
if ((_index > -2) && (_index < (_collection.InternalCount - 1)))
{
_index++;
_currentElement = _collection[_index];
return true;
}
else
{
_currentElement = null;
_index = -2; // -2 <=> reached the end.
return false;
}
}
else
{
throw new InvalidOperationException(SR.Enumerator_CollectionChanged);
}
}
/// <summary>
/// Gets the current Visual.
/// </summary>
object IEnumerator.Current
{
get
{
return this.Current;
}
}
/// <summary>
/// Gets the current Visual.
/// </summary>
public Visual CurrentView on GitHub (pinned to 81131a70a4)