dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'timeShift')

Error message

Value cannot be null. (Parameter 'timeShift')

What it means

The Buffer(source, timeSpan, timeShift) overload throws ArgumentNullException naming 'timeShift'. The guard is `timeShift < TimeSpan.Zero`, so the message "Value cannot be null" is misleading: the actual trigger is a negative time-shift value. timeShift controls how far apart successive buffer windows start, so it must be non-negative.

Solutions

  1. Clamp the shift: Math.Max(TimeSpan.Zero, timeShift) before calling Buffer.
  2. Review the computation producing timeShift for sign errors or subtracting unbounded values.
  3. Validate user/config supplied shift values at load time so negatives never reach the operator.

Example fix

// before
var shift = interval - measuredDrift; // can be negative
var buffer = source.Buffer(interval, shift);
// after
var shift = interval - measuredDrift;
if (shift < TimeSpan.Zero) shift = TimeSpan.Zero;
var buffer = source.Buffer(interval, shift);
Defensive patterns

Strategy: validation

Validate before calling

if (timeShift < TimeSpan.Zero) throw new ArgumentOutOfRangeException(nameof(timeShift), timeShift, "Time shift must be non-negative");
var buffered = source.Buffer(timeSpan, timeShift);

Type guard

static bool IsValidShift(TimeSpan shift) => shift >= TimeSpan.Zero;

Try / catch

try { var b = source.Buffer(timeSpan, timeShift); }
catch (ArgumentNullException ex) when (ex.ParamName == "timeShift") { log.Warn("Negative timeShift passed to Buffer"); timeShift = TimeSpan.Zero; }

Prevention

When it happens

Trigger: Calling source.Buffer(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(-2)) or passing a computed shift that went negative (e.g. shift = interval - drift with drift > interval).

Common situations: Adaptive scheduling logic where the shift is derived from measured latencies and can go negative under load, or config typos like '-2s'.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/Buffer.cs:92

                (timeSpan, scheduler),
                static async (source, state, observer) =>
                {
                    var (sink, timer) = await AsyncObserver.Buffer(observer, state.timeSpan, state.scheduler).ConfigureAwait(false);

                    var subscription = await source.SubscribeSafeAsync(sink).ConfigureAwait(false);

                    return StableCompositeAsyncDisposable.Create(subscription, timer);
                });
        }

        public static IAsyncObservable<IList<TSource>> Buffer<TSource>(this IAsyncObservable<TSource> source, TimeSpan timeSpan, TimeSpan timeShift)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (timeSpan < TimeSpan.Zero)
                throw new ArgumentNullException(nameof(timeSpan));
            if (timeShift < TimeSpan.Zero)
                throw new ArgumentNullException(nameof(timeShift));

            return CreateAsyncObservable<IList<TSource>>.From(
                source,
                (timeSpan, timeShift),
                static async (source, state, observer) =>
                {
                    var (sink, timer) = await AsyncObserver.Buffer(observer, state.timeSpan, state.timeShift).ConfigureAwait(false);

                    var subscription = await source.SubscribeSafeAsync(sink).ConfigureAwait(false);

                    return StableCompositeAsyncDisposable.Create(subscription, timer);
                });
        }

        public static IAsyncObservable<IList<TSource>> Buffer<TSource>(this IAsyncObservable<TSource> source, TimeSpan timeSpan, TimeSpan timeShift, IAsyncScheduler scheduler)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));

View on GitHub (pinned to 94b5d5ab91)