AvaloniaUI/Avalonia · error · ArgumentNullException

Value cannot be null. (Parameter 'tcs')

Error message

Value cannot be null. (Parameter 'tcs')

What it means

AnonymousObserver<T> has a constructor that adapts a TaskCompletionSource<T> into an IObserver<T> (OnNext=SetResult, OnError=SetException, OnCompleted=NoOp). It throws ArgumentNullException(nameof(tcs)) when tcs is null, because adapting a null TCS would later NullRef inside the observer callbacks. Fail-fast at construction.

Source

Thrown at src/Avalonia.Base/Reactive/AnonymousObserver.cs:21

using static Avalonia.Reactive.AnonymousObserverNonGenericHelper;

namespace Avalonia.Reactive;

/// <summary>
/// Class to create an <see cref="IObserver{T}"/> instance from delegate-based implementations of the On* methods.
/// </summary>
/// <typeparam name="T">The type of the elements in the sequence.</typeparam>
public class AnonymousObserver<T> : IObserver<T>
{
    private readonly Action<T> _onNext;
    private readonly Action<Exception> _onError;
    private readonly Action _onCompleted;

    public AnonymousObserver(TaskCompletionSource<T> tcs)
    {
        if (tcs is null)
        {
            throw new ArgumentNullException(nameof(tcs));
        }

        _onNext = tcs.SetResult;
        _onError = tcs.SetException;
        _onCompleted = NoOpCompleted;
    }
    
    public AnonymousObserver(Action<T> onNext, Action<Exception> onError, Action onCompleted)
    {
        _onNext = onNext ?? throw new ArgumentNullException(nameof(onNext));
        _onError = onError ?? throw new ArgumentNullException(nameof(onError));
        _onCompleted = onCompleted ?? throw new ArgumentNullException(nameof(onCompleted));
    }

    public AnonymousObserver(Action<T> onNext)
        : this(onNext, ThrowsOnError, NoOpCompleted)
    {
    }

View on GitHub (pinned to 11c5427268)

Solutions

  1. Ensure the TaskCompletionSource<T> is constructed (new TaskCompletionSource<T>()) before passing it.
  2. Null-check at the call site if the TCS comes from an optional/computed source and skip the subscription when null.
  3. Prefer Observable.ToTask / FirstAsync extensions over hand-rolled TCS-to-observer adapters where available.

Example fix

// before
var tcs = GetMaybeNullTcs();
source.Subscribe(new AnonymousObserver<T>(tcs)); // throws if null

// after
var tcs = new TaskCompletionSource<T>();
source.Subscribe(new AnonymousObserver<T>(tcs));
return tcs.Task;
Defensive patterns

Strategy: validation

Validate before calling

if (tcs is null) throw new InvalidOperationException("TCS required");
source.Subscribe(new AnonymousObserver<T>(tcs));

Type guard

bool HasTcs<T>(TaskCompletionSource<T>? tcs) => tcs is not null;

Prevention

When it happens

Trigger: Constructing new AnonymousObserver<T>(null) by passing a TaskCompletionSource variable that is null, then subscribing it to an IObservable<T>.

Common situations: Bridge code that subscribes an observer built from a TCS where the TCS was never initialized or was cleared after use, often in async-over-reactive adapters.

Related errors


AI-assisted analysis of AvaloniaUI/Avalonia@11c5427268 (2026-08-13). Data as JSON: /api/errors/551886d4ff2f8e25. Report an issue: GitHub.