dotnet/wpf · error · InvalidOperationException

throw new…

Error message

throw new InvalidOperationException(SR.Format(SR.AddedItemNotAtIndex, index));

What it means

While processing a Remove notification, EnumerableCollectionView verifies that the item reported as removed actually sits at the expected index in its internal snapshot before deleting it. If _snapshot[index] does not equal args.OldItems[i] (via ItemsControl.EqualsEx), the snapshot and source collection have diverged and AddedItemNotAtIndex is thrown.

Solutions

  1. Ensure every mutation of the source collection raises a matching, correctly-indexed CollectionChanged event.
  2. Make item equality (EqualsEx uses Equals) stable — don't mutate items' identity fields used by Equals.
  3. Raise Reset for bulk mutations so the view rebuilds its snapshot.
  4. Force a Refresh/rebind of the view to resynchronize the snapshot.

Example fix

// before
_items[0] = newItem; // silent mutation, later Remove at 0 mismatches snapshot
// after
_items[0] = newItem;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newItem, oldItem, 0));
Defensive patterns

Strategy: validation

Validate before calling

// before raising Remove, verify the item is still at the reported index
if (!Equals(collection[oldStartingIndex], removedItem))
    collectionChangedArgs = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);

Try / catch

// recover by forcing a rebuild
try { ProcessChange(args); } catch (InvalidOperationException) { view.Refresh(); }

Prevention

When it happens

Trigger: Source collection raised Remove with OldStartingIndex pointing at an item that differs from args.OldItems[i] in the view's snapshot — e.g. the source mutated its items without raising corresponding notifications, or raised events out of order.

Common situations: Custom collection that changes item order or content without raising change notifications while an ItemsControl is bound; missed events because the collection was modified before the view subscribed.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/dc36f7ffc11cc8b8. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/Data/EnumerableCollectionView.cs:426

                    }
                    else
                    {   // insert
                        for (int i = args.NewItems.Count - 1; i >= 0; --i)
                        {
                            _snapshot.Insert(args.NewStartingIndex, args.NewItems[i]);
                        }
                    }
                    break;

                case NotifyCollectionChangedAction.Remove:
                    if (args.OldStartingIndex < 0)
                        throw new InvalidOperationException(SR.RemovedItemNotFound);

                    for (int i = args.OldItems.Count - 1, index = args.OldStartingIndex + i; i >= 0; --i, --index)
                    {
                        if (!System.Windows.Controls.ItemsControl.EqualsEx(args.OldItems[i], _snapshot[index]))
                            // replace error message with a better one
                            throw new InvalidOperationException(SR.Format(SR.AddedItemNotAtIndex, index));
                        _snapshot.RemoveAt(index);
                    }
                    break;

                case NotifyCollectionChangedAction.Replace:
                    for (int i = args.NewItems.Count - 1, index = args.NewStartingIndex + i; i >= 0; --i, --index)
                    {
                        if (!System.Windows.Controls.ItemsControl.EqualsEx(args.OldItems[i], _snapshot[index]))
                            // replace error message with a better one
                            throw new InvalidOperationException(SR.Format(SR.AddedItemNotAtIndex, index));
                        _snapshot[index] = args.NewItems[i];
                    }
                    break;

                case NotifyCollectionChangedAction.Move:
                    if (args.NewStartingIndex < 0)
                        throw new InvalidOperationException(SR.CannotMoveToUnknownPosition);

View on GitHub (pinned to 81131a70a4)