dotnet/reactive · error · ArgumentNullException
comparer
Error message
comparer
What it means
System.Reactive throws ArgumentNullException immediately when the IEqualityComparer<TSource> passed to Contains is null. The comparer is mandatory on this overload because equality of TSource is undefined without it; the null check runs synchronously at the call site before delegating to the implementation. Passing null here instead of using the comparer-less overload is a common mistake.
Solutions
- Call the comparer-less overload source.Contains(value) when default EqualityComparer<T>.Default is desired
- Pass EqualityComparer<TSource>.Default explicitly instead of null
- Guard the comparer before calling: comparer != null ? source.Contains(v, comparer) : source.Contains(v)
- Fix the resolver that produced the null comparer and register/choose a valid one
Example fix
// before
var has = names.Contains("alice", (IEqualityComparer<string>)null);
// after
var has = names.Contains("alice", StringComparer.OrdinalIgnoreCase);
// or for default equality:
var has = names.Contains("alice"); Defensive patterns
Strategy: validation
Validate before calling
var has = comparer != null
? source.Contains(value, comparer)
: source.Contains(value); Type guard
bool HasComparer<T>(IEqualityComparer<T> comparer) => comparer != null || EqualityComparer<T>.Default != null;
Try / catch
try
{
var has = source.Contains(value, comparer);
}
catch (ArgumentNullException ex) when (ex.ParamName == "comparer")
{
// fall back to default equality
var has2 = source.Contains(value);
} Prevention
- Use the comparer-less Contains overload when default equality suffices
- Pass EqualityComparer<T>.Default instead of null for generic forwarding code
- Make config-driven comparer lookup return a default rather than null
- Never forward an optional comparer parameter directly into Rx operators without a null branch
When it happens
Trigger: Calling source.Contains(value, null) when intending the default-equality overload, or passing a comparer variable resolved from configuration/DI that was not registered.
Common situations: Generic helper code that always forwards a comparer parameter which is null in some paths; config-driven comparer selection failing to match a known name; refactors that added the comparer overload without branching on null.
Related errors
- Value cannot be null. (Parameter 'source')
- Value cannot be null. (Parameter 'scheduler')
- Value cannot be null. (Parameter 'comparer')
- Value cannot be null. (Parameter 'observer')
- predicate
AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15).
Data as JSON: /api/errors/8f24bfb20a7f88a7.
Report an issue: GitHub.
Appendix: source
Thrown at Rx.NET/Source/src/System.Reactive/Linq/Observable.Aggregates.cs:663
/// Determines whether an observable sequence contains a specified element by using a specified System.Collections.Generic.IEqualityComparer{T}.
/// </summary>
/// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
/// <param name="source">An observable sequence in which to locate a value.</param>
/// <param name="value">The value to locate in the source sequence.</param>
/// <param name="comparer">An equality comparer to compare elements.</param>
/// <returns>An observable sequence containing a single element determining whether the source sequence contains an element that has the specified value.</returns>
/// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="comparer"/> is null.</exception>
/// <remarks>The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior.</remarks>
public static IObservable<bool> Contains<TSource>(this IObservable<TSource> source, TSource value, IEqualityComparer<TSource> comparer)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
if (comparer == null)
{
throw new ArgumentNullException(nameof(comparer));
}
return s_impl.Contains(source, value, comparer);
}
#endregion
#region + Count +
/// <summary>
/// Returns an observable sequence containing an <see cref="int" /> that represents the total number of elements in an observable sequence.
/// </summary>
/// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
/// <param name="source">An observable sequence that contains elements to be counted.</param>
/// <returns>An observable sequence containing a single element with the number of elements in the input sequence.</returns>
/// <exception cref="ArgumentNullException"><paramref name="source"/> is null.</exception>
/// <exception cref="OverflowException">(Asynchronous) The number of elements in the source sequence is larger than <see cref="long.MaxValue"/>.</exception>
/// <remarks>The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior.</remarks>View on GitHub (pinned to 94b5d5ab91)