dotnet/reactive · error · ArgumentNullException
Value cannot be null. (Parameter 'subscribeAsync')
Error message
Value cannot be null. (Parameter 'subscribeAsync')
What it means
The ToLookup operator validates its delegate arguments eagerly when the operator is built. A null valueSelector means the operator cannot project elements to values once the lookup aggregation starts, so System.Reactive.Async throws ArgumentNullException(nameof(valueSelector)) immediately at subscription-wiring time rather than failing later inside the aggregation. This fail-fast contract mirrors LINQ's own argument guards.
Solutions
- Pass a non-null Func<TSource,TValue> as the third argument to ToLookup
- If the selector is computed, check it for null before calling ToLookup
- If a no-op projection is intended, use x => x or the appropriate identity/select overload instead of null
Example fix
// before var op = AsyncObservable.ToLookup(source, x => x.Key, (Func<Order, string>)null); // after var op = AsyncObservable.ToLookup(source, x => x.Key, x => x.Value);
Defensive patterns
Strategy: validation
Validate before calling
if (source is null) throw new ArgumentNullException(nameof(source)); if (keySelector is null) throw new ArgumentNullException(nameof(keySelector)); if (valueSelector is null) throw new ArgumentNullException(nameof(valueSelector));
Type guard
static bool IsValidToLookupArgs<TSource,TKey,TValue>(Func<TSource,ValueTask<TKey>> k, Func<TSource,ValueTask<TValue>> v) => k != null && v != null;
Try / catch
try
{
var op = AsyncObservable.ToLookup(observer, keySelector, valueSelector);
}
catch (ArgumentNullException ex) when (ex.ParamName == "valueSelector")
{
// supply a default selector or log configuration error
} Prevention
- Never pass null for Func delegates; use identity lambdas like x => x instead
- Validate optional delegate configuration at startup, not at pipeline composition
- Keep overload calls complete — prefer named arguments for clarity
When it happens
Trigger: Calling AsyncObservable.ToLookup(observer, keySelector, null) — the 3-argument overload with sync Func selectors, at ToLookup.cs:126.
Common situations: Passing the result of a conditional expression or a helper that returned null for the value selector; refactoring where a Func variable became null; reflection or DI-created delegates resolving to null.
Related errors
- Value cannot be null. (Parameter 'observer')
- Value cannot be null. (Parameter 'observer')
- Value cannot be null. (Parameter 'source')
- Value cannot be null. (Parameter 'onNextAsync')
- source
AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15).
Data as JSON: /api/errors/8245738bb9308e2d.
Report an issue: GitHub.
Appendix: source
Thrown at AsyncRx.NET/System.Reactive.Async/AsyncObservable.cs:15
// 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 AsyncObservable<T> : AsyncObservableBase<T>
{
private readonly Func<IAsyncObserver<T>, ValueTask<IAsyncDisposable>> _subscribeAsync;
public AsyncObservable(Func<IAsyncObserver<T>, ValueTask<IAsyncDisposable>> subscribeAsync)
{
_subscribeAsync = subscribeAsync ?? throw new ArgumentNullException(nameof(subscribeAsync));
}
protected override ValueTask<IAsyncDisposable> SubscribeAsyncCore(IAsyncObserver<T> observer)
{
if (observer == null)
throw new ArgumentNullException(nameof(observer));
return _subscribeAsync(observer);
}
}
}
View on GitHub (pinned to 94b5d5ab91)