dotnet/reactive · error · ArgumentNullException
Value cannot be null. (Parameter 'keySelector')
Error message
Value cannot be null. (Parameter 'keySelector')
What it means
GroupByUntil requires a keySelector delegate that maps each source element to a group key. The operator eagerly validates arguments when the overload with (source, keySelector, durationSelector, comparer) is called, and throws ArgumentNullException when keySelector is null. This fail-fast check prevents a null delegate from surfacing later inside the subscription pipeline.
Solutions
- Pass a non-null lambda as keySelector, e.g. x => x.Category
- Check the variable supplying the delegate for null before calling GroupByUntil
- If the key selector is optional by design, supply a default like _ => default(TKey) instead of null
Example fix
// before Func<Order, string> keySel = config.Mode == "cat" ? (Func<Order,string>)null : o => o.Id; var groups = source.GroupByUntil(keySel, g => Observable.Never<Unit>(), comparer); // after Func<Order, string> keySel = config.Mode == "cat" ? (Func<Order,string>)(o => o.Category) : o => o.Id; var groups = source.GroupByUntil(keySel, g => Observable.Never<Unit>(), comparer);
Defensive patterns
Strategy: validation
Validate before calling
if (keySelector == null) throw new ArgumentException("keySelector must be provided before calling GroupByUntil"); Type guard
bool HasKeySelector<TSource,TKey>(Func<TSource,TKey> f) => f is not null;
Try / catch
try { var groups = source.GroupByUntil(ks, ds, cmp); } catch (ArgumentNullException ex) when (ex.ParamName == "keySelector") { /* supply default selector or fail fast */ } Prevention
- Never store delegates in nullable fields without an initializer
- Prefer method groups or lambdas inline over nullable Func variables
- Enable nullable reference types (Func<TSource,TKey>?) makes null-flow visible at compile time
When it happens
Trigger: Calling AsyncObservable.GroupByUntil(source, null, durationSelector, comparer) with the 4-argument overload that accepts an IEqualityComparer<TKey>, passing null for the keySelector parameter.
Common situations: Storing the key selector in a variable that was never assigned (e.g. a nullable Func field defaulting to null), conditional delegation where a lambda is only set in some code paths, or refactoring that removed the lambda but left the call site intact.
Related errors
- Value cannot be null. (Parameter 'durationSelector')
- ArgumentNullException: source
- Value cannot be null. (Parameter 'comparer')
- elementSelector
- Value cannot be null. (Parameter 'source')
AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15).
Data as JSON: /api/errors/e3cdd1415d907c5e.
Report an issue: GitHub.
Appendix: source
Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/GroupByUntil.cs:36
if (source == null)
throw new ArgumentNullException(nameof(source));
if (keySelector == null)
throw new ArgumentNullException(nameof(keySelector));
if (durationSelector == null)
throw new ArgumentNullException(nameof(durationSelector));
return CreateAsyncObservable<IGroupedAsyncObservable<TKey, TSource>>.From(
source,
(keySelector, durationSelector),
static (source, state, observer) => GroupByUntilCore<TSource, TKey, TSource, TDuration>(source, observer, (o, d) => AsyncObserver.GroupByUntil(o, d, state.keySelector, state.durationSelector)));
}
public static IAsyncObservable<IGroupedAsyncObservable<TKey, TSource>> GroupByUntil<TSource, TKey, TDuration>(this IAsyncObservable<TSource> source, Func<TSource, TKey> keySelector, Func<IGroupedAsyncObservable<TKey, TSource>, IAsyncObservable<TDuration>> durationSelector, IEqualityComparer<TKey> comparer)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
if (keySelector == null)
throw new ArgumentNullException(nameof(keySelector));
if (durationSelector == null)
throw new ArgumentNullException(nameof(durationSelector));
if (comparer == null)
throw new ArgumentNullException(nameof(comparer));
return CreateAsyncObservable<IGroupedAsyncObservable<TKey, TSource>>.From(
source,
(keySelector, durationSelector, comparer),
static (source, state, observer) => GroupByUntilCore<TSource, TKey, TSource, TDuration>(source, observer, (o, d) => AsyncObserver.GroupByUntil(o, d, state.keySelector, state.durationSelector, state.comparer)));
}
public static IAsyncObservable<IGroupedAsyncObservable<TKey, TSource>> GroupByUntil<TSource, TKey, TDuration>(this IAsyncObservable<TSource> source, Func<TSource, TKey> keySelector, Func<IGroupedAsyncObservable<TKey, TSource>, IAsyncObservable<TDuration>> durationSelector, int capacity)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
if (keySelector == null)
throw new ArgumentNullException(nameof(keySelector));
if (durationSelector == null)View on GitHub (pinned to 94b5d5ab91)