dotnet/reactive · error · ArgumentNullException

throw new…

Error message

throw new ArgumentNullException(nameof(timeoutDurationSelector));

What it means

The Timeout(source, timeoutDurationSelector) overload throws ArgumentNullException because the timeoutDurationSelector delegate is null. This selector computes a per-item timeout stream, so it is mandatory for the operator. The check runs eagerly when the query is composed.

Solutions

  1. Pass a real selector, e.g. item => Observable.Timer(TimeSpan.FromSeconds(1))
  2. If the selector is optional, provide a default like _ => Observable.Never<TTimeout>()
  3. Guard the delegate for null before composing the query

Example fix

// before
source.Timeout(_timeoutSelector)
// after
var selector = _timeoutSelector ?? (_ => Observable.Timer(TimeSpan.FromSeconds(10)));
source.Timeout(selector)
Defensive patterns

Strategy: validation

Validate before calling

if (timeoutDurationSelector is null)
    throw new InvalidOperationException("timeoutDurationSelector must be provided.");
// or default it:
var selector = timeoutDurationSelector ?? (_ => Observable.Never<TTimeout>());

Type guard

static bool IsValidSelector<TSource, TTimeout>(Func<TSource, IObservable<TTimeout>> f) => f is not null;

Try / catch

try
{
    result = source.Timeout(selector);
}
catch (ArgumentNullException ex) when (ex.ParamName == "timeoutDurationSelector")
{
    result = source.Timeout(_ => Observable.Never<TTimeout>());
}

Prevention

When it happens

Trigger: Calling source.Timeout(selector) with a null Func<TSource, IObservable<TTimeout>, e.g. an optional selector parameter left at its default of null.

Common situations: Optional method parameters of type Func<T,...> defaulting to null and passed straight through; a strategy object whose selector delegate was never assigned; reflection-based invocation with missing configuration.

Understand the failure class

Related errors


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

Appendix: source

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

        /// Applies a timeout policy to the observable sequence based on a timeout duration computed for each element.
        /// If the next element isn't received within the computed duration starting from its predecessor, a TimeoutException is propagated to the observer.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
        /// <typeparam name="TTimeout">The type of the elements in the timeout sequences used to indicate the timeout duration for each element in the source sequence.</typeparam>
        /// <param name="source">Source sequence to perform a timeout for.</param>
        /// <param name="timeoutDurationSelector">Selector to retrieve an observable sequence that represents the timeout between the current element and the next element.</param>
        /// <returns>The source sequence with a TimeoutException in case of a timeout.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="timeoutDurationSelector"/> is null.</exception>
        public static IObservable<TSource> Timeout<TSource, TTimeout>(this IObservable<TSource> source, Func<TSource, IObservable<TTimeout>> timeoutDurationSelector)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            if (timeoutDurationSelector == null)
            {
                throw new ArgumentNullException(nameof(timeoutDurationSelector));
            }

            return s_impl.Timeout(source, timeoutDurationSelector);
        }

        /// <summary>
        /// Applies a timeout policy to the observable sequence based on a timeout duration computed for each element.
        /// If the next element isn't received within the computed duration starting from its predecessor, the other observable sequence is used to produce future messages from that point on.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements in the source sequence and the other sequence used upon a timeout.</typeparam>
        /// <typeparam name="TTimeout">The type of the elements in the timeout sequences used to indicate the timeout duration for each element in the source sequence.</typeparam>
        /// <param name="source">Source sequence to perform a timeout for.</param>
        /// <param name="timeoutDurationSelector">Selector to retrieve an observable sequence that represents the timeout between the current element and the next element.</param>
        /// <param name="other">Sequence to return in case of a timeout.</param>
        /// <returns>The source sequence switching to the other sequence in case of a timeout.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="timeoutDurationSelector"/> or <paramref name="other"/> is null.</exception>
        public static IObservable<TSource> Timeout<TSource, TTimeout>(this IObservable<TSource> source, Func<TSource, IObservable<TTimeout>> timeoutDurationSelector, IObservable<TSource> other)
        {

View on GitHub (pinned to 94b5d5ab91)