dotnet/reactive · error · ArgumentOutOfRangeException

throw new ArgumentOutOfRangeException(nameof(period));

Error message

throw new ArgumentOutOfRangeException(nameof(period));

What it means

Observable.Timer(TimeSpan dueTime, TimeSpan period) throws ArgumentOutOfRangeException when period is negative, because a repeating timer cannot have a negative interval between emissions. TimeSpan.Zero is allowed (indefinite repetitions as fast as the scheduler permits), but any value below Zero is rejected eagerly.

Solutions

  1. Pass a non-negative TimeSpan for period (use TimeSpan.Zero for continuous repetitions)
  2. Validate or clamp the configured interval: if (period < TimeSpan.Zero) period = TimeSpan.Zero
  3. Fix the calculation producing the negative TimeSpan (check operand order in subtractions)

Example fix

// before
var period = TimeSpan.FromSeconds(target) - TimeSpan.FromSeconds(current); // can be negative
var timer = Observable.Timer(dueTime, period);
// after
var period = TimeSpan.FromSeconds(Math.Max(0, target - current));
var timer = Observable.Timer(dueTime, period);
Defensive patterns

Strategy: validation

Validate before calling

if (period < TimeSpan.Zero)
    throw new ArgumentOutOfRangeException(nameof(period));
var timer = Observable.Timer(dueTime, period);

Type guard

bool isValid = period >= TimeSpan.Zero;

Try / catch

try
{
    timer = Observable.Timer(dueTime, period);
}
catch (ArgumentOutOfRangeException ex) when (ex.ParamName == "period")
{
    timer = Observable.Timer(dueTime, TimeSpan.Zero);
}

Prevention

When it happens

Trigger: Calling Observable.Timer(dueTime, TimeSpan.FromMilliseconds(-1)) or computing the period from an expression that yields a negative TimeSpan (e.g. subtracting timestamps in the wrong order, or a config value parsed with a negative sign).

Common situations: Polling intervals read from configuration where the value is negative or the subtraction of due dates inverts order; unit tests using negative delays; arithmetic like (a - b) where a < b.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Linq/Observable.Time.cs:2046

        /// <param name="dueTime">Absolute time at which to produce the value. If this value is less than or equal to DateTimeOffset.UtcNow, the timer will fire as soon as possible.</param>
        /// <returns>An observable sequence that produces a value at due time.</returns>
        public static IObservable<long> Timer(DateTimeOffset dueTime)
        {
            return s_impl.Timer(dueTime);
        }

        /// <summary>
        /// Returns an observable sequence that periodically produces a value after the specified initial relative due time has elapsed.
        /// </summary>
        /// <param name="dueTime">Relative time at which to produce the first value. If this value is less than or equal to TimeSpan.Zero, the timer will fire as soon as possible.</param>
        /// <param name="period">Period to produce subsequent values. If this value is equal to TimeSpan.Zero, the timer will recur as fast as possible.</param>
        /// <returns>An observable sequence that produces a value after due time has elapsed and then after each period.</returns>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="period"/> is less than TimeSpan.Zero.</exception>
        public static IObservable<long> Timer(TimeSpan dueTime, TimeSpan period)
        {
            if (period < TimeSpan.Zero)
            {
                throw new ArgumentOutOfRangeException(nameof(period));
            }

            return s_impl.Timer(dueTime, period);
        }

        /// <summary>
        /// Returns an observable sequence that periodically produces a value starting at the specified initial absolute due time.
        /// </summary>
        /// <param name="dueTime">Absolute time at which to produce the first value. If this value is less than or equal to DateTimeOffset.UtcNow, the timer will fire as soon as possible.</param>
        /// <param name="period">Period to produce subsequent values. If this value is equal to TimeSpan.Zero, the timer will recur as fast as possible.</param>
        /// <returns>An observable sequence that produces a value at due time and then after each period.</returns>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="period"/> is less than TimeSpan.Zero.</exception>
        public static IObservable<long> Timer(DateTimeOffset dueTime, TimeSpan period)
        {
            if (period < TimeSpan.Zero)
            {
                throw new ArgumentOutOfRangeException(nameof(period));
            }

View on GitHub (pinned to 94b5d5ab91)