dotnet/reactive · error · ArgumentNullException
Value cannot be null. (Parameter 'durationSelector')
Error message
Value cannot be null. (Parameter 'durationSelector')
What it means
GroupByUntil needs a durationSelector that maps each created group to an observable signaling when the group closes. In the (source, keySelector, durationSelector, comparer) overload, a null durationSelector throws ArgumentNullException immediately at call time. The check exists so group lifetime logic is always present before the pipeline is built.
Solutions
- Supply a duration selector, e.g. g => AsyncObservable.Never<TDuration>() for never-closing groups
- Verify the expression producing the duration selector is not null at the call site
- Use plain GroupBy instead if group lifetime should be unbounded
Example fix
// before var groups = source.GroupByUntil(x => x.Key, null, comparer); // after var groups = source.GroupByUntil(x => x.Key, g => AsyncObservable.Never<Unit>(), comparer);
Defensive patterns
Strategy: validation
Validate before calling
if (durationSelector == null) throw new ArgumentException("durationSelector is required by GroupByUntil; use AsyncObservable.Never<T>() for unbounded groups"); Type guard
bool HasDurationSelector<TSource,TKey,TD>(Func<IAsyncGroupedObservable<TKey,TSource>,IAsyncObservable<TD>> f) => f is not null;
Try / catch
try { var groups = source.GroupByUntil(ks, ds, cmp); } catch (ArgumentNullException ex) when (ex.ParamName == "durationSelector") { var groups = source.GroupBy(ks, cmp); } Prevention
- Remember GroupByUntil always needs a duration selector — there is no overload without one
- When porting from GroupBy, map 'no duration' to g => AsyncObservable.Never<Unit>()
- Avoid resolving delegates via reflection/config without a null check
When it happens
Trigger: Calling AsyncObservable.GroupByUntil(source, keySelector, null, comparer) with the comparer overload, omitting the Func<IGroupedAsyncObservable<TKey,TSource>, IAsyncObservable<TDuration>> argument.
Common situations: Porting from Rx.NET GroupBy (which has no duration selector) and dropping the argument, building the delegate via reflection/config that resolved to null, or passing a method group that failed to bind.
Related errors
- Value cannot be null. (Parameter 'keySelector')
- 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/ad62f2123cc933e0.
Report an issue: GitHub.
Appendix: source
Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/GroupByUntil.cs:38
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)
throw new ArgumentNullException(nameof(durationSelector));
if (capacity < 0)View on GitHub (pinned to 94b5d5ab91)