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
- Ensure the TaskCompletionSource<T> is constructed (new TaskCompletionSource<T>()) before passing it.
- Null-check at the call site if the TCS comes from an optional/computed source and skip the subscription when null.
- 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
- Always construct the TaskCompletionSource<T> before adapting it.
- Prefer framework ToTask/FirstAsync over hand-rolled TCS adapters.
- Null-check TCS fields from optional sources before subscribing.
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
- Value cannot be null. (Parameter 'onNext')
- Value cannot be null. (Parameter 'onError')
- Value cannot be null. (Parameter 'disposables')
- Value cannot be null. (Parameter 'item')
- Value cannot be null. (Parameter 'array')
AI-assisted analysis of AvaloniaUI/Avalonia@11c5427268 (2026-08-13).
Data as JSON: /api/errors/551886d4ff2f8e25.
Report an issue: GitHub.