dotnet/reactive · error · ArgumentOutOfRangeException

Specified argument was out of the range of valid values…

Error message

Specified argument was out of the range of valid values. (Parameter 'period')

What it means

ThreadPoolScheduler.SchedulePeriodic throws ArgumentOutOfRangeException when the period is negative (less than TimeSpan.Zero). Like the TaskPool variant, a strictly negative periodic interval is rejected as an invalid value before any other work is done.

Solutions

  1. Pass a zero or positive TimeSpan.
  2. Clamp negative computed periods to TimeSpan.Zero before the call.
  3. Fix the computation/parsing that produced the negative value.

Example fix

// before
var period = TimeSpan.FromMilliseconds(intervalMs); // intervalMs = -250
scheduler.SchedulePeriodic(state, period, next => next);
// after
var period = TimeSpan.FromMilliseconds(Math.Max(0, intervalMs));
scheduler.SchedulePeriodic(state, period, next => next);
Defensive patterns

Strategy: validation

Validate before calling

if (period < TimeSpan.Zero) throw new InvalidOperationException($"Negative period {period}");
scheduler.SchedulePeriodic(state, period, action);

Try / catch

try { scheduler.SchedulePeriodic(state, period, action); }
catch (ArgumentOutOfRangeException ex) when (ex.ParamName == "period") { log.LogError("Period must be non-negative, got {Period}", period); }

Prevention

When it happens

Trigger: Calling scheduler.SchedulePeriodic(state, TimeSpan.FromSeconds(-1), action) or any computed negative duration.

Common situations: Reversed subtraction for the interval, misparsed config values ('-5s'), or unit conversion mistakes when migrating from milliseconds/int APIs.

Related errors


AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15). Data as JSON: /api/errors/12ddf0e653d14067. Report an issue: GitHub.

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Concurrency/ThreadPoolScheduler.cs:132

            //
            return new StopwatchImpl();
        }

        /// <summary>
        /// Schedules a periodic piece of work, using a System.Threading.Timer object.
        /// </summary>
        /// <typeparam name="TState">The type of the state passed to the scheduled action.</typeparam>
        /// <param name="state">Initial state passed to the action upon the first iteration.</param>
        /// <param name="period">Period for running the work periodically.</param>
        /// <param name="action">Action to be executed, potentially updating the state.</param>
        /// <returns>The disposable object used to cancel the scheduled recurring action (best effort).</returns>
        /// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c>.</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="period"/> is less than zero.</exception>
        public IDisposable SchedulePeriodic<TState>(TState state, TimeSpan period, Func<TState, TState> action)
        {
            if (period < TimeSpan.Zero)
            {
                throw new ArgumentOutOfRangeException(nameof(period));
            }

            if (action == null)
            {
                throw new ArgumentNullException(nameof(action));
            }

            if (period == TimeSpan.Zero)
            {
                return new FastPeriodicTimer<TState>(state, action);
            }

            return new PeriodicTimer<TState>(state, period, action);
        }

        private sealed class FastPeriodicTimer<TState> : IDisposable
        {
            private TState _state;

View on GitHub (pinned to 94b5d5ab91)