dotnet/wpf · error · InvalidOperationException
throw new…
Error message
throw new InvalidOperationException(SR.CannotMoveToUnknownPosition);
What it means
When a Move notification arrives with NewStartingIndex < 0, EnumerableCollectionView cannot determine where the item(s) were moved to and throws CannotMoveToUnknownPosition. INotifyCollectionChanged requires Move events to carry a valid destination index.
Solutions
- Set NewStartingIndex to the post-move index when raising Move events (NotifyCollectionChangedAction.Move accepts a single index).
- Raise Reset when the destination index cannot be determined.
- Replace the Move with explicit Remove+Add events at known indices.
- Use ObservableCollection<T>.Move which always reports the correct index.
Example fix
// before RaiseEvent(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Move, item, -1)); // after RaiseEvent(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Move, item, newIndex));
Defensive patterns
Strategy: validation
Validate before calling
if (moveArgs.NewStartingIndex < 0)
moveArgs = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset); Try / catch
try { ApplyMove(args); } catch (InvalidOperationException) { view.Refresh(); } Prevention
- Always supply a valid destination index for Move events.
- Use ObservableCollection<T>.Move which reports correct indices.
- Fall back to Reset when the destination is unknown.
- Validate event args in a custom collection base class.
When it happens
Trigger: Source collection raises NotifyCollectionChangedAction.Move with args.NewStartingIndex == -1 (unknown position), processed by ProcessCollectionChanged on a bound view.
Common situations: Custom collections implementing Move by reusing a generic constructor or defaulting indices to -1; forwarding a Move event built from incomplete information.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- throw new…
- throw new…
- SR.CannotMoveToUnknownPosition
- SR.Format(SR.UnexpectedCollectionChangeAction, args.Action)
- SR.Format(SR.UnexpectedCollectionChangeAction, e.Action)
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/37e33b5b5f4a012b.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/Data/EnumerableCollectionView.cs:443
// 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);
if (args.OldStartingIndex < args.NewStartingIndex)
{
for (int i = args.OldItems.Count - 1,
oldIndex = args.OldStartingIndex + i,
newIndex = args.NewStartingIndex + i;
i >= 0;
--i, --oldIndex, --newIndex)
{
if (!System.Windows.Controls.ItemsControl.EqualsEx(args.OldItems[i], _snapshot[oldIndex]))
// replace error message with a better one
throw new InvalidOperationException(SR.Format(SR.AddedItemNotAtIndex, oldIndex));
_snapshot.Move(oldIndex, newIndex);
}
}
else
{
for (int i = 0,View on GitHub (pinned to 81131a70a4)