dotnet/reactive · error · ArgumentOutOfRangeException

period (Specified argument was out of the range of valid…

Error message

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

What it means

SchedulePeriodic<TState>(IScheduler, TState, TimeSpan, Func<TState,TState>) throws ArgumentOutOfRangeException when period < TimeSpan.Zero. A negative period is meaningless for a periodic timer; Rx rejects it up front instead of scheduling an invalid sequence of ticks. TimeSpan.Zero is allowed (period invoked immediately/repeatedly per scheduler semantics).

Solutions

  1. Clamp or validate the period before calling: if (period < TimeSpan.Zero) period = TimeSpan.Zero; or throw with your own message
  2. Fix the computation producing the negative TimeSpan (check the order of subtraction operands and units)
  3. If the interval comes from config, validate it at load time and reject negative values with a clear error

Example fix

// before
var period = nextRun - DateTime.Now; // can be negative
scheduler.SchedulePeriodic(state, period, s => s);

// after
var period = nextRun - DateTime.Now;
if (period < TimeSpan.Zero) period = TimeSpan.Zero;
scheduler.SchedulePeriodic(state, period, s => s);
Defensive patterns

Strategy: validation

Validate before calling

if (period < TimeSpan.Zero) throw new ArgumentOutOfRangeException(nameof(period), period, "Period must be non-negative");
scheduler.SchedulePeriodic(state, period, action);

Type guard

bool IsValidPeriod(TimeSpan p) => p >= TimeSpan.Zero;

Try / catch

try { scheduler.SchedulePeriodic(state, period, action); }
catch (ArgumentOutOfRangeException ex) when (ex.ParamName == "period") { /* clamp to Zero or report config error */ }

Prevention

When it happens

Trigger: Calling SchedulePeriodic with a TimeSpan computed to be negative, e.g. TimeSpan.FromSeconds(-1), a subtraction of DateTime values yielding negative duration, or deserialized/converted config values such as 'PT-5S' producing a negative TimeSpan.

Common situations: Configuration parsing errors where a delay/interval value is negative, subtracting a later timestamp from an earlier one to compute an interval, integer arithmetic overflow wrapping negative, or unit confusion (treating a value in seconds when it was milliseconds).

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Concurrency/Scheduler.Services.Emulation.cs:37

        /// </summary>
        /// <typeparam name="TState">The type of the state passed to the scheduled action.</typeparam>
        /// <param name="scheduler">The scheduler to run periodic work on.</param>
        /// <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="scheduler"/> or <paramref name="action"/> is <c>null</c>.</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="period"/> is less than <see cref="TimeSpan.Zero"/>.</exception>
        public static IDisposable SchedulePeriodic<TState>(this IScheduler scheduler, TState state, TimeSpan period, Func<TState, TState> action)
        {
            if (scheduler == null)
            {
                throw new ArgumentNullException(nameof(scheduler));
            }

            if (period < TimeSpan.Zero)
            {
                throw new ArgumentOutOfRangeException(nameof(period));
            }

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

            return SchedulePeriodic_(scheduler, state, period, action);
        }

        /// <summary>
        /// Schedules a periodic piece of work by dynamically discovering the scheduler's capabilities.
        /// If the scheduler supports periodic scheduling, the request will be forwarded to the periodic scheduling implementation.
        /// If the scheduler provides stopwatch functionality, the periodic task will be emulated using recursive scheduling with a stopwatch to correct for time slippage.
        /// Otherwise, the periodic task will be emulated using recursive scheduling.
        /// </summary>
        /// <typeparam name="TState">The type of the state passed to the scheduled action.</typeparam>
        /// <param name="scheduler">Scheduler to execute the action on.</param>

View on GitHub (pinned to 94b5d5ab91)