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 'timeSpan')

What it means

System.Reactive's public Buffer(source, timeSpan) overload validates its arguments eagerly at the call site before creating the underlying observable. It throws ArgumentOutOfRangeException when timeSpan is negative because a negative buffer window duration has no valid meaning. This fail-fast check surfaces the bug at the caller instead of deep inside the query.

Solutions

  1. Clamp or validate the TimeSpan before calling Buffer, e.g. Math.Max(TimeSpan.Zero, computed)
  2. Trace where the TimeSpan is computed and fix the arithmetic so the window is non-negative
  3. If the intent was an immediate close/open cycle, use TimeSpan.Zero, which is valid

Example fix

// before
var window = end - start; // can be negative if start > end
observable.Buffer(window);
// after
var window = end > start ? end - start : TimeSpan.Zero;
observable.Buffer(window);
Defensive patterns

Strategy: validation

Validate before calling

if (source == null) throw new ArgumentNullException(nameof(source));
if (timeSpan < TimeSpan.Zero) throw new ArgumentOutOfRangeException(nameof(timeSpan));

Type guard

static bool IsValidWindow(TimeSpan t) => t >= TimeSpan.Zero;

Try / catch

try { source.Buffer(window); }
catch (ArgumentOutOfRangeException ex) when (ex.ParamName == "timeSpan")
{
    logger.LogWarning("Invalid buffer window {Window}, falling back to 1s", window);
    source.Buffer(TimeSpan.FromSeconds(1));
}

Prevention

When it happens

Trigger: Calling Observable.Buffer(source, TimeSpan.Zero - someDelta) or passing a negative TimeSpan computed at runtime (e.g. a deadline that has already passed, or subtracting durations in the wrong order).

Common situations: Computing a window size from configuration or telemetry where the value can go negative; subtracting a later timestamp from an earlier one; typo like TimeSpan.FromSeconds(-1) in tests; unit misconversion (milliseconds vs seconds) yielding negative values.

Related errors


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

Appendix: source

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

        /// <param name="timeSpan">Length of each buffer.</param>
        /// <returns>An observable sequence of buffers.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> is null.</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="timeSpan"/> is less than TimeSpan.Zero.</exception>
        /// <remarks>
        /// Specifying a TimeSpan.Zero value for <paramref name="timeSpan"/> is not recommended but supported, causing the scheduler to create buffers as fast as it can.
        /// Because all source sequence elements end up in one of the buffers, some buffers won't have a zero time span. This is a side-effect of the asynchrony introduced
        /// by the scheduler, where the action to close the current buffer and to create a new buffer may not execute immediately, despite the TimeSpan.Zero due time.
        /// </remarks>
        public static IObservable<IList<TSource>> Buffer<TSource>(this IObservable<TSource> source, TimeSpan timeSpan)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

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

            return s_impl.Buffer(source, timeSpan);
        }

        /// <summary>
        /// Projects each element of an observable sequence into consecutive non-overlapping buffers which are produced based on timing information, using the specified scheduler to run timers.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements in the source sequence, and in the lists in the result sequence.</typeparam>
        /// <param name="source">Source sequence to produce buffers over.</param>
        /// <param name="timeSpan">Length of each buffer.</param>
        /// <param name="scheduler">Scheduler to run buffering timers on.</param>
        /// <returns>An observable sequence of buffers.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="scheduler"/> is null.</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="timeSpan"/> is less than TimeSpan.Zero.</exception>
        /// <remarks>
        /// Specifying a TimeSpan.Zero value for <paramref name="timeSpan"/> is not recommended but supported, causing the scheduler to create buffers as fast as it can.
        /// Because all source sequence elements end up in one of the buffers, some buffers won't have a zero time span. This is a side-effect of the asynchrony introduced

View on GitHub (pinned to 94b5d5ab91)