dotnet/wpf · error · InvalidOperationException

SR.Freezable_Reentrant

Error message

SR.Freezable_Reentrant

What it means

CheckReentrancy throws InvalidOperationException(SR.Freezable_Reentrant) when the collection is modified (Clear, Insert, Remove, RemoveAt, Add) while a previous CollectionChanged notification is still being dispatched (_monitor.Busy). This prevents mutating the collection from inside a change handler, which would corrupt in-flight notifications for other listeners.

Solutions

  1. Defer the mutation out of the handler: Dispatcher.BeginInvoke(() => collection.Add(...)) or queue the change and apply after the notification completes.
  2. Restructure so handlers never mutate the same collection they are notified about.
  3. Use a snapshot (ToList()) inside the handler and mutate the copy, applying the result later.

Example fix

// before
void OnCollectionChanged(...) { collection.Remove(badItem); } // reentrant
// after
void OnCollectionChanged(...) { Dispatcher.BeginInvoke(new Action(() => collection.Remove(badItem))); }
Defensive patterns

Strategy: try-catch

Validate before calling

bool canMutate = !isInsideCollectionChangedHandler;
if (!canMutate) queueMutation(() => collection.Add(item));

Type guard

null

Try / catch

try { collection.Add(item); }
catch (InvalidOperationException ex) when (isReentrancy(ex)) { Dispatcher.BeginInvoke(new Action(() => collection.Add(item))); }

Prevention

When it happens

Trigger: Calling Add/Remove/Clear/etc. from inside a CollectionChanged event handler or a PropertyChanged handler triggered synchronously by the same collection change — e.g. handler does collection.Remove(item) while the Add notification is still propagating.

Common situations: Event handlers that 'fix up' the collection in response to a change, chained two-way data binding where updating one collection mutates another in the same notification pass, re-entrant UI logic in collection callbacks.

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


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/FreezableCollection.cs:933

        ///         {
        ///             CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, item, index));
        ///         }
        /// </code>
        /// </remarks>
        private IDisposable BlockReentrancy()
        {
            _monitor.Enter();
            return _monitor;
        }

        /// <summary> Check and assert for reentrant attempts to change this collection. </summary>
        /// <exception cref="InvalidOperationException"> raised when changing the collection
        /// while another collection change is still being notified to other listeners </exception>
        private void CheckReentrancy()
        {
            if (_monitor.Busy)
            {
                throw new InvalidOperationException(SR.Freezable_Reentrant);
            }
        }

        #endregion ProtectedMethods

        //------------------------------------------------------
        //
        //  Internal Fields
        //
        //------------------------------------------------------

        #region Internal Fields

        internal List<T> _collection;
        internal uint _version = 0;

        #endregion Internal Fields

View on GitHub (pinned to 81131a70a4)