dotnet/wpf · error · InvalidOperationException

The Dispatcher failed to schedule a request for processing.

Error message

The Dispatcher failed to schedule a request for processing.

What it means

When Dispatcher fails to schedule a background work item for request processing (e.g. the ThreadPool or timer post fails), it consults BaseCompatibilityPreferences.HandleDispatcherRequestProcessingFailure. If the app opted into 'Throw', an InvalidOperationException is raised; 'Continue' swallows it and 'Reset' retries by clearing the posted-processing flag.

Solutions

  1. Change the preference to HandleDispatcherRequestProcessingFailureOptions.Reset or Continue if strict throwing is not required
  2. Set the preference as early as possible (it must be set before the Dispatcher is used, typically at startup)
  3. Investigate why scheduling failed: thread-pool exhaustion, AppDomain/runtime shutdown, or low memory
  4. Retry the Dispatcher operation after the transient condition clears

Example fix

// before
BaseCompatibilityPreferences.HandleDispatcherRequestProcessingFailure =
    BaseCompatibilityPreferences.HandleDispatcherRequestProcessingFailureOptions.Throw;
// after
BaseCompatibilityPreferences.HandleDispatcherRequestProcessingFailure =
    BaseCompatibilityPreferences.HandleDispatcherRequestProcessingFailureOptions.Reset;
Defensive patterns

Strategy: try-catch

Validate before calling

// verify the compatibility preference at startup
var mode = BaseCompatibilityPreferences.HandleDispatcherRequestProcessingFailure;
Debug.Assert(mode != BaseCompatibilityPreferences.HandleDispatcherRequestProcessingFailureOptions.Throw || Debugger.IsAttached);

Try / catch

try
{
    dispatcher.InvokeAsync(work);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("failed to schedule"))
{
    logger.LogError(ex, "Dispatcher request processing could not be scheduled");
}

Prevention

When it happens

Trigger: QueueUserWorkItem/Timer scheduling fails inside the Dispatcher's request-processing path while BaseCompatibilityPreferences.HandleDispatcherRequestProcessingFailure is set to HandleDispatcherRequestProcessingFailureOptions.Throw.

Common situations: Apps that set the compatibility switch for strict failure handling (common in test suites or reliability-sensitive services), especially under thread-pool starvation or when the runtime is shutting down.

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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Threading/Dispatcher.cs:2482

                // keep the list from growing too large
                // (although usually it will have only one entry)
                if (list.Count > 1000)
                {
                    // keep the earliest and latest failures
                    list.RemoveRange(100, list.Count - 200);
                    // acknowledge the gap
                    list.Insert(100, "... entries removed to conserve memory ...");
                }
            }

            // handle the failure, according to app's preference
            switch (BaseCompatibilityPreferences.HandleDispatcherRequestProcessingFailure)
            {
                case BaseCompatibilityPreferences.HandleDispatcherRequestProcessingFailureOptions.Continue:
                    break;
                case BaseCompatibilityPreferences.HandleDispatcherRequestProcessingFailureOptions.Throw:
                    throw new InvalidOperationException(SR.DispatcherRequestProcessingFailed);
                case BaseCompatibilityPreferences.HandleDispatcherRequestProcessingFailureOptions.Reset:
                    _postedProcessingType = PROCESS_NONE;
                    break;
            }
        }

        internal void PromoteTimers(int currentTimeInTicks)
        {
            try
            {
                List<DispatcherTimer> timers = null;
                long timersVersion = 0;

                lock(_instanceLock)
                {
                    if(!_hasShutdownFinished) // Could be a non-dispatcher thread, lock to read
                    {
                        if(_dueTimeFound && _dueTimeInTicks - currentTimeInTicks <= 0)

View on GitHub (pinned to 81131a70a4)