dotnet/wpf · error · NotSupportedException

SR.Format(SR.UnexpectedCollectionChangeAction, e.Action)

Error message

SR.Format(SR.UnexpectedCollectionChangeAction, e.Action)

What it means

ListCollectionView.ProcessCollectionChange encountered a NotifyCollectionChangedAction value outside Add/Remove/Replace/Move/Reset when replaying an underlying-collection change into its shadow copy. Since .NET has no other defined actions, this indicates a corrupt or custom-injected event argument. It is thrown as NotSupportedException to signal the view cannot process the change.

Solutions

  1. Fix the custom INotifyCollectionChanged source to only raise the five standard NotifyCollectionChangedAction values.
  2. Construct event args via NotifyCollectionChangedEventArgs.Add/Remove/Replace/Move/Reset factories instead of custom subclasses.
  3. Catch NotSupportedException around collection mutations if the event source is out of your control, and resync the view by calling Refresh().

Example fix

// before (custom collection)
OnCollectionChanged(new NotifyCollectionChangedEventArgs((NotifyCollectionChangedAction)99));
// after
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
Defensive patterns

Strategy: type-guard

Validate before calling

if (!Enum.IsDefined(typeof(NotifyCollectionChangedAction), args.Action)) throw new InvalidOperationException("Invalid collection change action");

Type guard

bool IsValidAction(NotifyCollectionChangedAction a) => a is NotifyCollectionChangedAction.Add or NotifyCollectionChangedAction.Remove or NotifyCollectionChangedAction.Replace or NotifyCollectionChangedAction.Move or NotifyCollectionChangedAction.Reset;

Try / catch

try { view.Refresh(); } catch (NotSupportedException) { view.Refresh(); /* resync view from source */ }

Prevention

When it happens

Trigger: A CollectionChanged handler path in ProcessCollectionChange receives e.Action not equal to Add, Remove, Replace, Move, or Reset — practically only from a custom INotifyCollectionChanged implementation passing an invalid/cast enum value.

Common situations: Custom observable collections that manually raise CollectionChanged with hand-crafted or unvalidated NotifyCollectionChangedEventArgs; aggressive enum casts like (NotifyCollectionChangedAction)99; interop code reusing event args across libraries.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Data/ListCollectionView.cs:2411

                    {
                        // allow the ShadowCollection to throw the IndexOutOfRangeException
                        // if the item is not found.
                        tempIndex = ShadowCollection.IndexOf(e.OldItems[0]);
                        ShadowCollection[tempIndex] = e.NewItems[0];
                    }
                    break;
                case NotifyCollectionChangedAction.Move:
                    tempIndex = e.OldStartingIndex;
                    if (tempIndex < 0)
                    {
                        tempIndex = ShadowCollection.IndexOf(e.NewItems[0]);
                    }
                    ShadowCollection.RemoveAt(tempIndex);
                    ShadowCollection.Insert(e.NewStartingIndex, e.NewItems[0]);
                    break;

                default:
                    throw new NotSupportedException(SR.Format(SR.UnexpectedCollectionChangeAction, e.Action));
            }
        }

        // returns true if this ListCollectionView has sort descriptions,
        // without tripping off lazy creation of .SortDescriptions collection
        internal bool HasSortDescriptions
        {
            get { return ((_sort != null) && (_sort.Count > 0)); }
        }

        // return an appropriate comparer.   Common logic used by ListCollectionView
        // and by CollectionViewGroupInternal.
        internal static IComparer PrepareComparer(IComparer customSort, SortDescriptionCollection sort, Func<object, CollectionView> lazyGetCollectionView, object state)
        {
            if (customSort != null)
            {
                return customSort;
            }

View on GitHub (pinned to 81131a70a4)