dotnet/reactive · error · ArgumentNullException
keySelector
Error message
keySelector
What it means
AsyncObservable.GroupByUntil (2-argument overload) throws ArgumentNullException when the keySelector delegate is null. The key selector assigns each element to a group and is captured in the operator state, so it must be provided. This guard executes synchronously when the operator is constructed.
Solutions
- Pass a concrete key selector such as x => x.GroupKey.
- Validate delegates in your wrapper methods before forwarding.
- Avoid optional key-selector parameters; require them in the signature.
Example fix
// before
public static IOpped Group<T>(IAsyncObservable<T> s, Func<T, string> k = null)
=> s.GroupByUntil(k, g => g.Take(1));
// after
public static IOpped Group<T>(IAsyncObservable<T> s, Func<T, string> k)
=> s.GroupByUntil(k, g => g.Take(1)); Defensive patterns
Strategy: validation
Validate before calling
if (keySelector is null) throw new ArgumentNullException(nameof(keySelector));
Type guard
bool HasKey<T,TKey>(Func<T,TKey>? k) => k is not null;
Try / catch
try { var grouped = source.GroupByUntil(k, d); }
catch (ArgumentNullException ex) when (ex.ParamName == "keySelector") { /* supply key selector */ } Prevention
- Require key selectors as mandatory parameters in wrapper APIs.
- Never forward optional delegates without a null check.
- Test operator wrappers with null delegates to confirm guard behavior.
When it happens
Trigger: Calling GroupByUntil with a null Func<TSource, TKey>, e.g. when the selector is supplied by a caller of a wrapper method that did not validate, or an expression tree converted to null.
Common situations: Public wrapper APIs forwarding an optional keySelector without checks; lambda built from reflection that failed; copied code where the selector line was deleted.
Related errors
- source
- Value cannot be null. (Parameter 'elementSelector')
- Value cannot be null. (Parameter 'source')
- action
- action (Parameter 'action')
AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15).
Data as JSON: /api/errors/514e681f808e2f6b.
Report an issue: GitHub.
Appendix: source
Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/GroupByUntil.cs:21
// See the LICENSE file in the project root for more information.
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Reactive.Disposables;
using System.Reactive.Subjects;
using System.Threading;
using System.Threading.Tasks;
namespace System.Reactive.Linq
{
public partial class AsyncObservable
{
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)
{
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)View on GitHub (pinned to 94b5d5ab91)