dotnet/reactive · error · ArgumentOutOfRangeException

interval

Error message

interval

What it means

The Sample<TSource>(IObservable<TSource>, TimeSpan) operator throws ArgumentOutOfRangeException when the interval is negative (interval < TimeSpan.Zero). A sampling interval must be zero or positive; a negative due time is meaningless for a scheduler. Zero is allowed (samples as fast as the scheduler allows).

Solutions

  1. Validate the interval before calling Sample and clamp/reject negatives.
  2. Fix the interval computation so it cannot go negative (e.g., Math.Max(TimeSpan.Zero, diff)).
  3. If you meant 'sample as often as possible', pass TimeSpan.Zero explicitly rather than a negative value.

Example fix

// before
var interval = TimeSpan.FromSeconds(endSeconds - startSeconds); // can be negative
var sampled = source.Sample(interval); // ArgumentOutOfRangeException

// after
var interval = TimeSpan.FromSeconds(Math.Max(0, endSeconds - startSeconds));
var sampled = source.Sample(interval);
Defensive patterns

Strategy: validation

Validate before calling

if (interval < TimeSpan.Zero)
    interval = TimeSpan.Zero; // or reject
var sampled = source.Sample(interval);

Type guard

static bool IsValidInterval(TimeSpan interval) => interval >= TimeSpan.Zero;

Try / catch

try
{
    var sampled = source.Sample(interval);
}
catch (ArgumentOutOfRangeException ex)
{
    // log interval value; fall back to a default interval
    var sampled = source.Sample(TimeSpan.FromSeconds(1));
}

Prevention

When it happens

Trigger: Calling Observable.Sample(source, TimeSpan.FromSeconds(-1)) or any TimeSpan built from a negative value (e.g., subtracting DateTime values in the wrong order, or a config value parsed with a leading minus).

Common situations: Computing the interval from a date difference (later - earlier reversed); reading a negative interval from app settings; sign error when converting seconds to TimeSpan.From*; user-supplied config accepted without validation.

Related errors


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

Appendix: source

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

        /// <param name="source">Source sequence to sample.</param>
        /// <param name="interval">Interval at which to sample. If this value is equal to TimeSpan.Zero, the scheduler will continuously sample the stream.</param>
        /// <returns>Sampled observable sequence.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> is null.</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="interval"/> is less than TimeSpan.Zero.</exception>
        /// <remarks>
        /// Specifying a TimeSpan.Zero value for <paramref name="interval"/> doesn't guarantee all source sequence elements will be preserved. This is a side-effect
        /// of the asynchrony introduced by the scheduler, where the sampling action may not execute immediately, despite the TimeSpan.Zero due time.
        /// </remarks>
        public static IObservable<TSource> Sample<TSource>(this IObservable<TSource> source, TimeSpan interval)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

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

            return s_impl.Sample(source, interval);
        }

        /// <summary>
        /// Samples the observable sequence at each interval, using the specified scheduler to run sampling timers.
        /// Upon each sampling tick, the latest element (if any) in the source sequence during the last sampling interval is sent to the resulting sequence.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
        /// <param name="source">Source sequence to sample.</param>
        /// <param name="interval">Interval at which to sample. If this value is equal to TimeSpan.Zero, the scheduler will continuously sample the stream.</param>
        /// <param name="scheduler">Scheduler to run the sampling timer on.</param>
        /// <returns>Sampled observable sequence.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="scheduler"/> is null.</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="interval"/> is less than TimeSpan.Zero.</exception>
        /// <remarks>
        /// Specifying a TimeSpan.Zero value for <paramref name="interval"/> doesn't guarantee all source sequence elements will be preserved. This is a side-effect

View on GitHub (pinned to 94b5d5ab91)