dotnet/reactive · error · ArgumentNullException
comparer (Parameter 'comparer')
Error message
comparer (Parameter 'comparer')
What it means
The protected ScheduledItem<TAbsolute>.ctor(dueTime, comparer) throws ArgumentNullException when comparer is null. The comparer orders work items inside priority-queue-based schedulers (e.g. VirtualTimeScheduler, PriorityQueue) and is required for every enqueue, so the base constructor validates it immediately.
Solutions
- Pass Comparer<TAbsolute>.Default when no custom ordering is needed.
- Implement or register an IComparer<TAbsolute> and ensure it is non-null before constructing.
- In subclass constructors, guard: 'base(dueTime, comparer ?? Comparer<TAbsolute>.Default)'.
- Check DI registration for the comparer service.
Example fix
// before
protected MyItem(DateTimeOffset dueTime, IComparer<DateTimeOffset> comparer) : base(dueTime, comparer) { }
// after
protected MyItem(DateTimeOffset dueTime, IComparer<DateTimeOffset> comparer)
: base(dueTime, comparer ?? Comparer<DateTimeOffset>.Default) { } Defensive patterns
Strategy: validation
Validate before calling
if (comparer == null)
comparer = Comparer<TAbsolute>.Default; Type guard
bool HasComparer<TAbsolute>(IComparer<TAbsolute> c) => c is not null;
Try / catch
try
{
var item = new MyScheduledItem(dueTime, comparer);
}
catch (ArgumentNullException ex) when (ex.ParamName == "comparer")
{
var item = new MyScheduledItem(dueTime, Comparer<TAbsolute>.Default);
} Prevention
- Default to Comparer<T>.Default in subclass constructors.
- Never resolve comparers to null; provide a registered default implementation.
- Unit-test custom ScheduledItem subclasses with an explicitly supplied comparer.
When it happens
Trigger: Writing a custom ScheduledItem subclass that calls base(dueTime, null), or constructing built-in ScheduledItem variants through helper code that resolves an IComparer<TAbsolute> dynamically and gets null.
Common situations: Custom virtual-time schedulers for unit tests where a Comparer was forgotten; generics with default-comparer helpers returning null instead of Comparer<T>.Default; DI-injected comparer services not registered.
Related errors
- threadFactory (Parameter 'threadFactory')
- action (Parameter 'action')
- scheduler (Parameter 'scheduler')
- action (Parameter 'action')
- scheduler (Parameter 'scheduler')
AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15).
Data as JSON: /api/errors/207cc0d4cc84da13.
Report an issue: GitHub.
Appendix: source
Thrown at Rx.NET/Source/src/System.Reactive/Concurrency/ScheduledItem.cs:30
/// </summary>
/// <typeparam name="TAbsolute">Absolute time representation type.</typeparam>
#pragma warning disable CA1033, CA1063, CA1816 // (Overridable IDisposable.) This is a specialized base type, and it would be inappropriate to encourage anyone to build derived types that do more in Dispose.
public abstract class ScheduledItem<TAbsolute> : IScheduledItem<TAbsolute>, IComparable<ScheduledItem<TAbsolute>>, IDisposable
where TAbsolute : IComparable<TAbsolute>
{
private SingleAssignmentDisposableValue _disposable;
private readonly IComparer<TAbsolute> _comparer;
/// <summary>
/// Creates a new scheduled work item to run at the specified time.
/// </summary>
/// <param name="dueTime">Absolute time at which the work item has to be executed.</param>
/// <param name="comparer">Comparer used to compare work items based on their scheduled time.</param>
/// <exception cref="ArgumentNullException"><paramref name="comparer"/> is <c>null</c>.</exception>
protected ScheduledItem(TAbsolute dueTime, IComparer<TAbsolute> comparer)
{
DueTime = dueTime;
_comparer = comparer ?? throw new ArgumentNullException(nameof(comparer));
}
/// <summary>
/// Gets the absolute time at which the item is due for invocation.
/// </summary>
public TAbsolute DueTime { get; }
/// <summary>
/// Invokes the work item.
/// </summary>
public void Invoke()
{
if (!_disposable.IsDisposed)
{
_disposable.Disposable = InvokeCore();
}
}
View on GitHub (pinned to 94b5d5ab91)