dotnet/wpf · error · ArgumentOutOfRangeException

timeout

Error message

timeout

What it means

Dispatcher.Invoke validates the TimeSpan timeout argument and throws ArgumentOutOfRangeException (message shown as the parameter name 'timeout') when the timeout is negative and not exactly TimeSpan.FromMilliseconds(-1). -1 is the sentinel meaning 'infinite timeout'; any other negative value is rejected.

Solutions

  1. Pass TimeSpan.FromMilliseconds(-1) for an infinite timeout, or a non-negative TimeSpan.
  2. Clamp the timeout: if (ts < TimeSpan.Zero) ts = TimeSpan.FromMilliseconds(-1);
  3. Fix the calculation producing the negative TimeSpan (elapsed deadlines, subtraction order).
  4. Use the Invoke overloads without a timeout parameter when a default is acceptable.

Example fix

// before
dispatcher.Invoke(action, DispatcherPriority.Normal, TimeSpan.FromSeconds(-1));
// after
dispatcher.Invoke(action, DispatcherPriority.Normal, TimeSpan.FromMilliseconds(-1)); // infinite
Defensive patterns

Strategy: validation

Validate before calling

if (timeout < TimeSpan.Zero && timeout != TimeSpan.FromMilliseconds(-1))
    timeout = TimeSpan.FromMilliseconds(-1); // or clamp to Zero
dispatcher.Invoke(work, priority, timeout);

Prevention

When it happens

Trigger: Calling Dispatcher.Invoke(cb, priority, timeout, ...) with a negative TimeSpan such as TimeSpan.FromSeconds(-1) or TimeSpan.FromMilliseconds(-500); passing a computed TimeSpan that came out negative.

Common situations: Computing a remaining timeout from a deadline that already elapsed; typos like -TimeSpan.FromSeconds(5); migrating code that used 0/-1 int milliseconds into TimeSpan overloads with a stray negative.

Understand the failure class

Related errors


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

Appendix: source

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

        ///     A cancellation token that can be used to cancel the operation.
        ///     If the operation has not started, it will be aborted when the
        ///     cancellation token is canceled.  If the operation has started,
        ///     the operation can cooperate with the cancellation request.
        /// </param>
        /// <param name="timeout">
        ///     The minimum amount of time to wait for the operation to start.
        ///     Once the operation has started, it will complete before this method
        ///     returns.
        /// </param>
        public void Invoke(Action callback, DispatcherPriority priority, CancellationToken cancellationToken, TimeSpan timeout)
        {
            ArgumentNullException.ThrowIfNull(callback);
            ValidatePriority(priority, "priority");

            if( timeout.TotalMilliseconds < 0 &&
                timeout != TimeSpan.FromMilliseconds(-1))
            {
                throw new ArgumentOutOfRangeException(nameof(timeout));
            }

            // Fast-Path: if on the same thread, and invoking at Send priority,
            // and the cancellation token is not already canceled, then just
            // call the callback directly.
            if(!cancellationToken.IsCancellationRequested && priority == DispatcherPriority.Send && CheckAccess())
            {
                SynchronizationContext oldSynchronizationContext = SynchronizationContext.Current;

                try
                {
                    DispatcherSynchronizationContext newSynchronizationContext;
                    if(BaseCompatibilityPreferences.GetReuseDispatcherSynchronizationContextInstance())
                    {
                        newSynchronizationContext = _defaultDispatcherSynchronizationContext;
                    }
                    else
                    {

View on GitHub (pinned to 81131a70a4)