dotnet/reactive · error · ArgumentNullException
Value cannot be null. (Parameter 'onCompletedAsync')
Error message
Value cannot be null. (Parameter 'onCompletedAsync')
What it means
UnsafeAsyncObserver's constructor requires a non-null onCompletedAsync delegate and throws ArgumentNullException when it is null. The library intentionally has no default completion handler so that subscription authors always define completion behavior explicitly. The guard fires immediately in the constructor, before any events are delivered.
Solutions
- Pass a non-null completion delegate, e.g. () => ValueTask.CompletedTask
- Supply all three delegates at every UnsafeAsyncObserver construction site
- Create a factory/builder with sensible defaults to avoid hand-rolling the three arguments each time
Example fix
// before
var observer = new UnsafeAsyncObserver<int>(
x => ValueTask.CompletedTask,
ex => ValueTask.CompletedTask,
null);
// after
var observer = new UnsafeAsyncObserver<int>(
x => ValueTask.CompletedTask,
ex => ValueTask.CompletedTask,
() => ValueTask.CompletedTask); Defensive patterns
Strategy: validation
Validate before calling
if (onCompletedAsync == null) throw new ArgumentException("onCompletedAsync delegate is required"); Type guard
static bool IsValidObserver<T>(Func<T, ValueTask> n, Func<Exception, ValueTask> e, Func<ValueTask> c) => n != null && e != null && c != null;
Try / catch
try { var obs = new UnsafeAsyncObserver<int>(onNext, onError, onCompleted); } catch (ArgumentNullException ex) { /* ex.ParamName == "onCompletedAsync" */ } Prevention
- Define completion handling even when it is trivial: () => ValueTask.CompletedTask
- Centralize observer construction in a helper that fills in defaults
- Check constructor argument count/order when refactoring
When it happens
Trigger: Calling new UnsafeAsyncObserver<T>(onNextAsync, onErrorAsync, null) — passing a null Func<ValueTask> as the third constructor argument.
Common situations: Building an observer that only cares about values or errors and forgetting completion; generating observers via code that conditionally omits the completion callback; converting from Rx observers where OnCompleted is implemented but was not wired through.
Related errors
- Value cannot be null. (Parameter 'onErrorAsync')
- Value cannot be null. (Parameter 'error')
- ArgumentNullException
- source
- observer
AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15).
Data as JSON: /api/errors/a670e916e29fee4a.
Report an issue: GitHub.
Appendix: source
Thrown at AsyncRx.NET/System.Reactive.Async/Internal/UnsafeAsyncObserver.cs:19
// 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.Threading.Tasks;
namespace System.Reactive
{
public class UnsafeAsyncObserver<T> : IAsyncObserver<T>
{
private readonly Func<T, ValueTask> _onNextAsync;
private readonly Func<Exception, ValueTask> _onErrorAsync;
private readonly Func<ValueTask> _onCompletedAsync;
public UnsafeAsyncObserver(Func<T, ValueTask> onNextAsync, Func<Exception, ValueTask> onErrorAsync, Func<ValueTask> onCompletedAsync)
{
_onNextAsync = onNextAsync ?? throw new ArgumentNullException(nameof(onNextAsync));
_onErrorAsync = onErrorAsync ?? throw new ArgumentNullException(nameof(onErrorAsync));
_onCompletedAsync = onCompletedAsync ?? throw new ArgumentNullException(nameof(onCompletedAsync));
}
public ValueTask OnCompletedAsync() => _onCompletedAsync();
public ValueTask OnErrorAsync(Exception error) => _onErrorAsync(error ?? throw new ArgumentNullException(nameof(error)));
public ValueTask OnNextAsync(T value) => _onNextAsync(value);
}
}
View on GitHub (pinned to 94b5d5ab91)