dotnet/wpf · error · InvalidOperationException
SR.Enumerator_CollectionChanged
Error message
SR.Enumerator_CollectionChanged
What it means
FreezableCollection<T>.Enumerator.MoveNext throws InvalidOperationException(SR.Enumerator_CollectionChanged) when the collection's version changed since the enumerator was created (_version != _list._version). Enumerators are fail-fast: any Add/Remove/Insert/RemoveAt/Clear between MoveNext calls invalidates the enumerator.
Solutions
- Iterate over a snapshot: foreach (var item in collection.ToList()) so mutations affect the copy.
- Collect items to remove in a list, then apply Remove/RemoveAt after the loop.
- For reverse-index removal loops, iterate from Count-1 down to 0 using the indexer instead of foreach.
- Avoid cross-thread mutation during enumeration; synchronize access.
Example fix
// before
foreach (var item in collection)
if (IsBad(item)) collection.Remove(item); // throws
// after
foreach (var item in collection.ToList())
if (IsBad(item)) collection.Remove(item); Defensive patterns
Strategy: type-guard
Validate before calling
var snapshot = collection.ToList(); // version-stable copy
foreach (var item in snapshot) { /* safe to mutate original now */ } Type guard
static bool IsCurrent(Enumerator e) => !e.IsStale(); // prefer snapshot pattern instead
Try / catch
try { foreach (var item in collection) Process(item); }
catch (InvalidOperationException) { foreach (var item in collection.ToList()) Process(item); } Prevention
- Snapshot with ToList() before iterating if you may mutate
- Never Add/Remove inside foreach over the same collection
- Collect pending changes and apply after the loop
- Synchronize cross-thread access
When it happens
Trigger: foreach over a FreezableCollection while modifying it inside the loop body (Add/Remove/Clear), or a CollectionChanged handler mutates the collection during the same enumeration.
Common situations: Removing items while iterating to 'clean up', a change handler triggered mid-foreach that mutates the collection, multi-threaded access where one thread enumerates while another mutates.
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/b5c95b1e6b98838d.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/FreezableCollection.cs:1022
{
_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)