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
- Clamp or validate the TimeSpan before calling Buffer, e.g. Math.Max(TimeSpan.Zero, computed)
- Trace where the TimeSpan is computed and fix the arithmetic so the window is non-negative
- 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
- Always wrap computed TimeSpans in Math.Max(TimeSpan.Zero, ...) before passing to time-based Rx operators
- Use TimeSpan.From* factory methods with validated inputs instead of raw constructor arithmetic
- Add Debug.Assert(t >= TimeSpan.Zero) at duration-producing boundaries
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
- period (Parameter 'period')
- Specified argument was out of the range of valid values…
- throw new ArgumentOutOfRangeException(nameof(dueTime));
- Specified argument was out of the range of valid values…
- Specified argument was out of the range of valid values…
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 introducedView on GitHub (pinned to 94b5d5ab91)