dotnet/reactive · error · ArgumentOutOfRangeException

duration

Error message

duration

What it means

The time-based Skip<TSource>(IObservable<TSource>, TimeSpan) operator throws ArgumentOutOfRangeException when the duration is negative (duration < TimeSpan.Zero). Skipping a negative amount of time is meaningless; zero is allowed and skips nothing.

Solutions

  1. Clamp the duration: Math.Max(TimeSpan.Zero, computed) before calling Skip.
  2. Validate config/user input so negative durations are rejected upstream.
  3. Fix the timestamp arithmetic that produced the negative value.

Example fix

// before
var skipBy = end - start; // may be negative
var result = source.Skip(skipBy); // ArgumentOutOfRangeException

// after
var skipBy = end > start ? end - start : TimeSpan.Zero;
var result = source.Skip(skipBy);
Defensive patterns

Strategy: validation

Validate before calling

if (duration < TimeSpan.Zero)
    throw new ArgumentException($"duration must be >= 0, was {duration}");
var result = source.Skip(duration);

Type guard

static bool IsValidDuration(TimeSpan duration) => duration >= TimeSpan.Zero;

Try / catch

try
{
    var result = source.Skip(duration);
}
catch (ArgumentOutOfRangeException)
{
    var result = source; // skip nothing as a safe fallback
}

Prevention

When it happens

Trigger: Calling source.Skip(TimeSpan.FromSeconds(-2)) — any negative TimeSpan as the duration, typically from inverted date arithmetic or an unvalidated config value.

Common situations: Computing duration from timestamps in the wrong order; a config value like '-5s' parsed without validation; arithmetic on TimeSpan that underflows.

Related errors


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

Appendix: source

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

        /// <para>
        /// Specifying a TimeSpan.Zero value for <paramref name="duration"/> doesn't guarantee no elements will be dropped from the start of the source sequence.
        /// This is a side-effect of the asynchrony introduced by the scheduler, where the action that causes callbacks from the source sequence to be forwarded
        /// may not execute immediately, despite the TimeSpan.Zero due time.
        /// </para>
        /// <para>
        /// Errors produced by the source sequence are always forwarded to the result sequence, even if the error occurs before the <paramref name="duration"/>.
        /// </para>
        /// </remarks>
        public static IObservable<TSource> Skip<TSource>(this IObservable<TSource> source, TimeSpan duration)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

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

            return s_impl.Skip(source, duration);
        }

        /// <summary>
        /// Skips elements for the specified duration from the start of the observable source sequence, using the specified scheduler to run timers.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
        /// <param name="source">Source sequence to skip elements for.</param>
        /// <param name="duration">Duration for skipping elements from the start of the sequence.</param>
        /// <param name="scheduler">Scheduler to run the timer on.</param>
        /// <returns>An observable sequence with the elements skipped during the specified duration from the start of the source sequence.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="scheduler"/> is null.</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="duration"/> is less than TimeSpan.Zero.</exception>
        /// <remarks>
        /// <para>
        /// Specifying a TimeSpan.Zero value for <paramref name="duration"/> doesn't guarantee no elements will be dropped from the start of the source sequence.

View on GitHub (pinned to 94b5d5ab91)