dotnet/wpf · error · InvalidOperationException

SR.Format(SR.AccessCollectionAfterShutDown, collection)

Error message

SR.Format(SR.AccessCollectionAfterShutDown, collection)

What it means

BindingOperations.AccessCollection throws InvalidOperationException(SR.AccessCollectionAfterShutDown) when the WPF ViewManager (a dispatcher-bound singleton) is null, meaning the Dispatcher has already shut down. Any binding-collection access routed through BindingOperations after dispatcher shutdown cannot be honored, since the view manager that serializes access no longer exists.

Solutions

  1. Check dispatcher lifetime before accessing the collection: if (dispatcher.HasShutdownStarted || dispatcher.HasShutdownFinished) skip the update.
  2. Stop background producers on shutdown (CancellationToken, dispatcher ShutdownStarted event) so no collection access happens after teardown.
  3. Marshal pending work to run before shutdown completes, or detach collections from bindings before closing the app.

Example fix

// before
await Task.Run(() => LoadData());
BindingOperations.AccessCollection(collection, () => collection.Add(item), true); // may throw at shutdown

// after
if (!dispatcher.HasShutdownStarted && !dispatcher.HasShutdownFinished)
{
    BindingOperations.AccessCollection(collection, () => collection.Add(item), true);
}
Defensive patterns

Strategy: validation

Validate before calling

var d = collection is ICollection c
    ? Dispatcher.CurrentDispatcher : Dispatcher.CurrentDispatcher;
if (d.HasShutdownStarted || d.HasShutdownFinished)
    return; // skip collection access after shutdown

Type guard

bool IsDispatcherAlive(Dispatcher d) => d != null && !d.HasShutdownStarted && !d.HasShutdownFinished;

Try / catch

try { BindingOperations.AccessCollection(col, work, true); }
catch (InvalidOperationException) { /* dispatcher shut down; abandon or log */ }

Prevention

When it happens

Trigger: Calling BindingOperations.AccessCollection (or APIs like CollectionView refresh/cross-thread collection access routed through it) from a background thread or during application teardown after Dispatcher.InvokeShutdown/Exit, when ViewManager.Current returns null.

Common situations: Background worker threads still pushing collection updates while the app is closing; asynchronous continuations (await/Dispatcher.BeginInvoke callbacks) firing after window close/dispatcher shutdown; unit tests that dispose the dispatcher but then touch bound collections.

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/b9b2afd3f6787bfd. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Data/BindingOperations.cs:364

        ///         int index = 3;
        ///         int result = 0;
        ///         BindingOperations.AccessCollection(
        ///             _collection,
        ///             () => { result = _collection[index]; },
        ///             false);             // read-access
        ///     }
        /// Note that the access method refers to local variables (index, result)
        /// of MyMethod, as well as to an instance variable (_collection) of the
        /// 'this' object.
        /// </notes>
        public static void AccessCollection(
                            IEnumerable collection,
                            Action accessMethod,
                            bool writeAccess)
        {
            ViewManager vm = ViewManager.Current;
            if (vm == null)
                throw new InvalidOperationException(SR.Format(SR.AccessCollectionAfterShutDown, collection));

            vm.AccessCollection(collection, accessMethod, writeAccess);
        }

        /// <summary>
        /// Returns a list of all binding expressions that are:
        ///     a) top-level (do not belong to a parent MultiBindingExpression or BindingGroup)
        ///     b) source-updating (binding mode is TwoWay or OneWayToSource)
        ///     c) currently dirty or invalid
        /// and d) attached to a descendant of the given DependencyObject (if non-null).
        /// These are the bindings that may need attention before executing a command.
        /// </summary>
        public static ReadOnlyCollection<BindingExpressionBase> GetSourceUpdatingBindings(DependencyObject root)
        {
            List<BindingExpressionBase> list = DataBindEngine.CurrentDataBindEngine.CommitManager.GetBindingsInScope(root);
            return new ReadOnlyCollection<BindingExpressionBase>(list);
        }

View on GitHub (pinned to 81131a70a4)