dotnet/wpf · error · InvalidOperationException
SR.Enumerator_CollectionChanged
Error message
SR.Enumerator_CollectionChanged
What it means
DrawingCollection's Enumerator.MoveNext throws InvalidOperationException (SR.Enumerator_CollectionChanged) when the collection's version stamp changed since the enumerator was created. WPF collections use fail-fast enumeration: any Add/Insert/Remove invalidates outstanding enumerators instead of risking undefined behavior.
Solutions
- Collect items to change into a separate list during iteration, then apply Add/Remove after the loop.
- Use an index-based for loop iterating backwards when removing items.
- Snapshot the collection first: foreach (var d in collection.ToArray()) so the enumerator operates on a copy.
Example fix
// before
foreach (var d in collection) { if (d is PenRemoved) collection.Remove(d); } // throws
// after
foreach (var d in collection.OfType<PenRemoved>().ToList()) collection.Remove(d); Defensive patterns
Strategy: try-catch
Validate before calling
var snapshot = collection.Cast<object>().ToArray(); // enumerate the snapshot, not the live collection
Type guard
null
Try / catch
try { foreach (var d in collection) { /* ... */ } } catch (InvalidOperationException ex) when (ex.Message.Contains("changed")) { /* restart with a snapshot */ } Prevention
- Never Add/Remove inside foreach over a WPF Freezable collection
- Batch mutations: collect changes, apply after the loop
- Use ToArray()/ToList() snapshots for read passes
- Marshal cross-thread mutations to the UI thread before enumerating
When it happens
Trigger: Iterating a DrawingCollection with foreach while adding or removing items inside the loop body; modifying the collection from another thread or from an event handler fired during iteration.
Common situations: Removing a drawing when a condition is met mid-foreach; UI code that mutates a DrawingGroup's children inside a rendering/layout callback while a LINQ/foreach pass is in progress.
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/106a957a30540c70.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/DrawingCollection.cs:832
{
_list.ReadPreamble();
if (_version == _list._version)
{
if (_index > -2 && _index < _list._collection.Count - 1)
{
_current = _list._collection[++_index];
return true;
}
else
{
_index = -2; // -2 indicates "past the end"
return false;
}
}
else
{
throw new InvalidOperationException(SR.Enumerator_CollectionChanged);
}
}
/// <summary>
/// Sets the enumerator to its initial position, which is before the
/// first element in the collection.
/// </summary>
public void Reset()
{
_list.ReadPreamble();
if (_version == _list._version)
{
_index = -1;
}
else
{
throw new InvalidOperationException(SR.Enumerator_CollectionChanged);View on GitHub (pinned to 81131a70a4)