dotnet/reactive · error · ArgumentNullException

ArgumentNullException

Error message

ArgumentNullException

What it means

The extension method AsyncObservable.Timestamp(source) throws ArgumentNullException when the source IAsyncObservable<TSource> is null. Rx-style operators validate the source at the operator call so subscription-time failures point to the actual problem instead of a NullReferenceException deep inside SubscribeSafeAsync.

Solutions

  1. Ensure the upstream observable is non-null before calling Timestamp; use AsyncObservable.Empty<TSource>() instead of null for 'no events'.
  2. Add a null check (ArgumentNullException.ThrowIfNull(source)) at the boundary that produces the observable.
  3. Fix the producing method to never return null; return an empty or failed observable.

Example fix

// before
var src = pipeline.Build(); // may return null
var ts = src.Timestamp();

// after
var src = pipeline.Build() ?? AsyncObservable.Empty<MyEvent>();
var ts = src.Timestamp();
Defensive patterns

Strategy: validation

Validate before calling

if (source is null)
    throw new InvalidOperationException("Timestamp requires a non-null source observable.");

Type guard

bool HasSource<T>(IAsyncObservable<T>? source) => source is not null;

Try / catch

try
{
    return source.Timestamp();
}
catch (ArgumentNullException ex) when (ex.ParamName == "source")
{
    return AsyncObservable.Empty<Timestamped<T>>().Timestamp();
}

Prevention

When it happens

Trigger: Chaining .Timestamp() on an observable variable that is null, e.g. a method returned null instead of an empty observable, or a conditional pipeline assembled the source lazily.

Common situations: Factory methods that return null on failure instead of AsyncObservable.Empty<T>(); nullable fields captured before initialization; switching branches where one branch forgets to assign the observable.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/Timestamp.cs:14

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT License.
// See the LICENSE file in the project root for more information. 

using System.Reactive.Concurrency;

namespace System.Reactive.Linq
{
    public partial class AsyncObservable
    {
        public static IAsyncObservable<Timestamped<TSource>> Timestamp<TSource>(this IAsyncObservable<TSource> source)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));

            return Create<TSource, Timestamped<TSource>>(source, static (source, observer) => source.SubscribeSafeAsync(AsyncObserver.Timestamp(observer)));
        }

        public static IAsyncObservable<Timestamped<TSource>> Timestamp<TSource>(this IAsyncObservable<TSource> source, IClock clock)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (clock == null)
                throw new ArgumentNullException(nameof(clock));

            return CreateAsyncObservable<Timestamped<TSource>>.From(
                source,
                clock,
                static (source, clock, observer) => source.SubscribeSafeAsync(AsyncObserver.Timestamp(observer, clock)));
        }
    }

View on GitHub (pinned to 94b5d5ab91)