dotnet/wpf · error · InvalidOperationException
SR.Enumerator_CollectionChanged
Error message
SR.Enumerator_CollectionChanged
What it means
TransformCollection.Enumerator.MoveNext() throws this InvalidOperationException when the underlying collection's version changed after the enumerator was created. WPF Freezable collections are versioned; any add/remove invalidates active enumerators because the documented enumerator contract requires throwing instead of returning stale or skipped data.
Solutions
- Collect items to remove/add first, then apply the mutations after the foreach loop completes.
- Iterate over a snapshot instead: foreach (Transform t in collection.ToArray()) — use System.Linq.
- Replace the whole loop with a for loop over indices going backwards when removing items.
- Wrap the enumeration in try/catch (InvalidOperationException) if the mutation is unavoidable and stale-iteration is acceptable.
Example fix
// before
foreach (Transform t in transformCollection)
{
if (t is TranslateTransform) transformCollection.Remove(t); // throws on next MoveNext
}
// after
var toRemove = transformCollection.OfType<TranslateTransform>().ToList();
foreach (var t in toRemove)
{
transformCollection.Remove(t);
} Defensive patterns
Strategy: try-catch
Validate before calling
// Before mutating while iterating, snapshot: bool willMutate = true; var snapshot = willMutate ? transformCollection.ToArray() : null;
Type guard
static bool IsEnumerationValid(TransformCollection c, TransformCollection.Enumerator e) => true; // no public version access; prefer snapshotting static bool HasItems(TransformCollection c) => c != null && c.Count > 0;
Try / catch
try
{
foreach (Transform t in transformCollection) { /* ... */ }
}
catch (InvalidOperationException ex) when (ex.Message.Contains("changed"))
{
// collection was modified during enumeration; retry with snapshot
foreach (Transform t in transformCollection.ToArray()) { /* ... */ }
} Prevention
- Never mutate a TransformCollection inside foreach over the same collection.
- Use ToList()/ToArray() snapshots when mutation during iteration is possible.
- Batch UI-thread mutations outside animation/rendering callbacks.
- Iterate backwards with an index-based for loop when removing items.
When it happens
Trigger: Calling foreach or GetEnumerator() on a TransformCollection, then adding, removing, inserting, or clearing items (e.g. Add/Remove/Insert/Clear) before MoveNext() advances past the end of the modified region; the version check (_version != _list._version) fails inside MoveNext.
Common situations: Modifying a TransformGroup's Children collection while iterating it in the same loop; a rendering/animation callback mutating the collection during UI-thread enumeration; copying items out with a for-each while another handler edits the collection.
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
- SR.Enumerator_CollectionChanged
- SR.Enumerator_CollectionChanged
- SR.Enumerator_CollectionChanged
- SR.Enumerator_CollectionChanged
- InvalidOperationException
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/ebe953d4ea6bdb56.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Generated/TransformCollection.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)