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

TaskPoolScheduler.SchedulePeriodic throws ArgumentOutOfRangeException when the period is less than TimeSpan.Zero (negative). Periodic scheduling requires a non-negative TimeSpan; the library normalizes zero/negative semantics but treats a strictly negative period as an invalid value. The check runs before the action null-check.

Solutions

  1. Pass a non-negative TimeSpan as the period.
  2. Clamp the computed period before the call, e.g. period < TimeSpan.Zero ? TimeSpan.Zero : period.
  3. Fix the source of the negative duration (reversed operands, bad parsing).

Example fix

// before
var period = end - start; // start > end => negative
scheduler.SchedulePeriodic(state, period, next => next);
// after
var period = end > start ? end - start : TimeSpan.FromSeconds(1);
scheduler.SchedulePeriodic(state, period, next => next);
Defensive patterns

Strategy: validation

Validate before calling

if (period < TimeSpan.Zero) throw new InvalidOperationException($"period must be >= 0, was {period}");
scheduler.SchedulePeriodic(state, period, action);

Try / catch

try { scheduler.SchedulePeriodic(state, period, action); }
catch (ArgumentOutOfRangeException ex) when (ex.ParamName == "period") { log.LogError("Negative period {Period}", period); }

Prevention

When it happens

Trigger: Calling scheduler.SchedulePeriodic(state, TimeSpan.FromXxx(-n), action) with any negative duration, e.g. a computed interval that turned negative.

Common situations: Computing the period by subtraction (end - start where start > end), parsing durations from config with a sign error, or converting from another unit with a wrong scale.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Concurrency/TaskPoolScheduler.cs:288

            //
            return new StopwatchImpl();
        }

        /// <summary>
        /// Schedules a periodic piece of work by running a platform-specific timer to create tasks periodically.
        /// </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 <see cref="TimeSpan.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));
            }

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

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

            private readonly TimeSpan _period;
            private readonly TaskFactory _taskFactory;
            private readonly Func<TState, TState> _action;
            private readonly AsyncLock _gate = new();

View on GitHub (pinned to 94b5d5ab91)