dotnet/reactive · error · ArgumentNullException

source

Error message

source

What it means

The Sample<TSource>(IObservable<TSource>, TimeSpan) operator throws ArgumentNullException when the source observable is null. Rx operators validate all reference arguments eagerly at call time so a bad composition fails immediately at the call site instead of surfacing as a null reference when subscribing. This is a programmer error, not a runtime condition.

Solutions

  1. Ensure the source observable is assigned before calling Sample; trace where the null reference originates.
  2. Guard the call site: if (source == null) throw or substitute Observable.Empty<TSource>().
  3. Fix the producing code so it never returns null — return Observable.Empty<TSource>() instead.

Example fix

// before
IObservable<int> src = GetStream(); // may return null
var sampled = src.Sample(TimeSpan.FromSeconds(1)); // ArgumentNullException

// after
var src = GetStream() ?? Observable.Empty<int>();
var sampled = src.Sample(TimeSpan.FromSeconds(1));
Defensive patterns

Strategy: validation

Validate before calling

if (source == null)
    throw new ArgumentException("source must not be null before calling Sample");
if (interval < TimeSpan.Zero)
    throw new ArgumentException("interval must be non-negative");
var sampled = source.Sample(interval);

Type guard

static bool IsValidSampleInput<TSource>(IObservable<TSource> source, TimeSpan interval) =>
    source != null && interval >= TimeSpan.Zero;

Try / catch

try
{
    var sampled = source.Sample(interval);
}
catch (ArgumentNullException) { /* source was null — fix producer */ }
catch (ArgumentOutOfRangeException) { /* negative interval */ }

Prevention

When it happens

Trigger: Calling Observable.Sample<TSource>(null, someTimeSpan) — i.e., the source argument is a null IObservable<TSource> reference, typically from an uninitialized field, a failed factory method, or a dictionary miss.

Common situations: Storing an IObservable in a field that was never assigned; a config-driven factory returning null; chaining LINQ-style operators where an earlier method returned null instead of an empty sequence; refactoring where a DI container failed to inject the observable.

Related errors


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

Appendix: source

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

        /// <summary>
        /// Samples the observable sequence at each interval.
        /// Upon each sampling tick, the latest element (if any) in the source sequence during the last sampling interval is sent to the resulting sequence.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
        /// <param name="source">Source sequence to sample.</param>
        /// <param name="interval">Interval at which to sample. If this value is equal to TimeSpan.Zero, the scheduler will continuously sample the stream.</param>
        /// <returns>Sampled observable sequence.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> is null.</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="interval"/> is less than TimeSpan.Zero.</exception>
        /// <remarks>
        /// Specifying a TimeSpan.Zero value for <paramref name="interval"/> doesn't guarantee all source sequence elements will be preserved. This is a side-effect
        /// of the asynchrony introduced by the scheduler, where the sampling action may not execute immediately, despite the TimeSpan.Zero due time.
        /// </remarks>
        public static IObservable<TSource> Sample<TSource>(this IObservable<TSource> source, TimeSpan interval)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

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

            return s_impl.Sample(source, interval);
        }

        /// <summary>
        /// Samples the observable sequence at each interval, using the specified scheduler to run sampling timers.
        /// Upon each sampling tick, the latest element (if any) in the source sequence during the last sampling interval is sent to the resulting sequence.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
        /// <param name="source">Source sequence to sample.</param>
        /// <param name="interval">Interval at which to sample. If this value is equal to TimeSpan.Zero, the scheduler will continuously sample the stream.</param>
        /// <param name="scheduler">Scheduler to run the sampling timer on.</param>

View on GitHub (pinned to 94b5d5ab91)