dotnet/reactive · error · ArgumentOutOfRangeException

window

Error message

window

What it means

The Replay(source, selector, window) overload throws ArgumentOutOfRangeException because the window TimeSpan was negative. The window defines how long past elements stay replayable in the SequentialReplayAsyncSubject, so only TimeSpan.Zero or positive durations are valid. The library rejects negatives eagerly at the call site.

Solutions

  1. Pass a non-negative TimeSpan (TimeSpan.Zero is allowed).
  2. Clamp before calling: window < TimeSpan.Zero ? TimeSpan.Zero : window.
  3. Fix the computation or configuration source that produced the negative duration.

Example fix

// before
var replayed = source.Replay(selector, expiry - now); // can be negative
// after
var window = expiry > now ? expiry - now : TimeSpan.Zero;
var replayed = source.Replay(selector, window);
Defensive patterns

Strategy: validation

Validate before calling

if (window < TimeSpan.Zero) throw new InvalidOperationException("window must be >= TimeSpan.Zero");
// safe to call source.Replay(selector, window);

Type guard

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

Try / catch

try { var r = source.Replay(selector, window); }
catch (ArgumentOutOfRangeException ex) when (ex.ParamName == "window") { /* clamp to TimeSpan.Zero and retry */ }

Prevention

When it happens

Trigger: Calling source.Replay(selector, TimeSpan.FromSeconds(-5)) or passing a TimeSpan computed from a subtraction/deserialized config value that ended up negative.

Common situations: Window read from configuration with an invalid unit conversion (ms vs s producing negative values); TimeSpan arithmetic that underflows (later - earlier with reversed operands); user-supplied duration not validated upstream.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/Replay.cs:158

                throw new ArgumentNullException(nameof(source));
            if (selector == null)
                throw new ArgumentNullException(nameof(selector));
            if (bufferSize < 0)
                throw new ArgumentOutOfRangeException(nameof(bufferSize));
            if (scheduler == null)
                throw new ArgumentNullException(nameof(scheduler));

            return Multicast(source, () => new SequentialReplayAsyncSubject<TSource>(bufferSize, scheduler), selector);
        }

        public static IAsyncObservable<TResult> Replay<TSource, TResult>(this IAsyncObservable<TSource> source, Func<IAsyncObservable<TSource>, IAsyncObservable<TResult>> selector, TimeSpan window)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (selector == null)
                throw new ArgumentNullException(nameof(selector));
            if (window < TimeSpan.Zero)
                throw new ArgumentOutOfRangeException(nameof(window));

            return Multicast(source, () => new SequentialReplayAsyncSubject<TSource>(window), selector);
        }

        public static IAsyncObservable<TResult> Replay<TSource, TResult>(this IAsyncObservable<TSource> source, Func<IAsyncObservable<TSource>, IAsyncObservable<TResult>> selector, TimeSpan window, IAsyncScheduler scheduler)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (selector == null)
                throw new ArgumentNullException(nameof(selector));
            if (window < TimeSpan.Zero)
                throw new ArgumentOutOfRangeException(nameof(window));
            if (scheduler == null)
                throw new ArgumentNullException(nameof(scheduler));

            return Multicast(source, () => new SequentialReplayAsyncSubject<TSource>(window, scheduler), selector);
        }

View on GitHub (pinned to 94b5d5ab91)