dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'observer')

Error message

Value cannot be null. (Parameter 'observer')

What it means

AverageInt32 is the internal AsyncObserver factory that builds the per-item averaging observer for int sequences. It throws ArgumentNullException ('Value cannot be null.', Parameter 'observer') when the downstream IAsyncObserver<double> sink is null. End users normally never call this directly; it is reached through AsyncObserver.Average/Sum-style operators, so a null here means the operator composition passed a null observer internally or you invoked AsyncObserver.AverageInt32 manually with null.

Solutions

  1. If calling AsyncObserver.AverageInt32 directly, pass the actual downstream IAsyncObserver<double> you received, never null.
  2. In custom operator code, ensure you forward the observer supplied by SubscribeSafeAsync/Create rather than a separately declared (possibly null) variable.
  3. Add a null guard before the call and throw a more descriptive exception naming your own operator for easier diagnosis.
  4. If you hit this via the public Average() operator only, report/check for a library bug — the public surface always passes a non-null observer.

Example fix

// before
IAsyncObserver<double> sink = null;
var avgObserver = AsyncObserver.AverageInt32(sink); // ArgumentNullException

// after
IAsyncObserver<double> sink = downstreamObserver ?? throw new ArgumentNullException(nameof(downstreamObserver));
var avgObserver = AsyncObserver.AverageInt32(sink);
Defensive patterns

Strategy: validation

Validate before calling

if (downstream == null) throw new ArgumentNullException(nameof(downstream), "AverageInt32 requires a non-null IAsyncObserver<double>");
var avgObserver = AsyncObserver.AverageInt32(downstream);

Type guard

static bool IsValidObserver(IAsyncObserver<double>? observer) => observer is not null;

Try / catch

try { var obs = AsyncObserver.AverageInt32(downstream); }
catch (ArgumentNullException ex) when (ex.ParamName == "observer") { logger.LogError(ex, "null observer passed to AverageInt32 composition"); throw; }

Prevention

When it happens

Trigger: Directly calling AsyncObserver.AverageInt32(null) (e.g. building a custom subscription/observer pipeline); writing a custom operator that forwards a null observer into AverageInt32; a mis-implemented CreateAsyncObservable shim that hands a null downstream observer to the operator lambda.

Common situations: Custom operator authors wiring AsyncObserver composition helpers by hand and forgetting to pass the real downstream observer; passing a nullable observer field that was assigned after subscription; copy-pasted operator boilerplate where the observer variable was renamed to a null-valued parameter.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/Average.cs:12

// 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. 

namespace System.Reactive.Linq
{
    public partial class AsyncObserver
    {
        public static IAsyncObserver<int> AverageInt32(IAsyncObserver<double> observer)
        {
            if (observer == null)
                throw new ArgumentNullException(nameof(observer));

            var sum = 0L;
            var count = 0L;

            return Create<int>(
                async x =>
                {
                    try
                    {
                        checked
                        {
                            sum += x;
                            count++;
                        }
                    }
                    catch (Exception ex)
                    {
                        await observer.OnErrorAsync(ex).ConfigureAwait(false);

View on GitHub (pinned to 94b5d5ab91)