dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'loopScheduler')

Error message

Value cannot be null. (Parameter 'loopScheduler')

What it means

TakeLast(source, duration, timerScheduler, loopScheduler) throws ArgumentNullException when loopScheduler is null. The loop scheduler is used to run the buffering/concurrency loop of the operator and must be provided in this overload. Validation happens immediately at the call site.

Solutions

  1. Pass a non-null loop scheduler (e.g. Scheduler.Default or ImmediateScheduler.Instance)
  2. Register/initialize the loop scheduler in your DI container or test setup
  3. Use the TakeLast(source, duration) overload that defaults both schedulers

Example fix

// before
var res = source.TakeLast(TimeSpan.FromSeconds(5), timerScheduler, null);
// after
var res = source.TakeLast(TimeSpan.FromSeconds(5), timerScheduler, Scheduler.Default);
Defensive patterns

Strategy: validation

Validate before calling

if (loopScheduler == null) throw new ArgumentNullException(nameof(loopScheduler));

Type guard

bool HasLoopScheduler(IScheduler s) => s is not null;

Try / catch

try { var res = source.TakeLast(dur, timer, loop); }
catch (ArgumentNullException ex) when (ex.ParamName == "loopScheduler") { /* supply default scheduler */ }

Prevention

When it happens

Trigger: Calling TakeLast(source, TimeSpan.FromSeconds(5), timerScheduler, null); passing a null scheduler obtained from configuration or a factory that failed.

Common situations: Test harness that supplies a TestScheduler for the timer but forgets the loop scheduler; DI container missing a second scheduler registration; conditional scheduler resolution returning null.

Related errors


AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15). Data as JSON: /api/errors/67641984c313072f. Report an issue: GitHub.

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Linq/Observable.Time.cs:1290

        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            if (duration < TimeSpan.Zero)
            {
                throw new ArgumentOutOfRangeException(nameof(duration));
            }

            if (timerScheduler == null)
            {
                throw new ArgumentNullException(nameof(timerScheduler));
            }

            if (loopScheduler == null)
            {
                throw new ArgumentNullException(nameof(loopScheduler));
            }

            return s_impl.TakeLast(source, duration, timerScheduler, loopScheduler);
        }

        #endregion

        #region + TakeLastBuffer +

        /// <summary>
        /// Returns a list with the elements within the specified duration from the end of the observable source sequence.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
        /// <param name="source">Source sequence to take elements from.</param>
        /// <param name="duration">Duration for taking elements from the end of the sequence.</param>
        /// <returns>An observable sequence containing a single list with the elements taken during the specified duration from the end of the source sequence.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> is null.</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="duration"/> is less than TimeSpan.Zero.</exception>

View on GitHub (pinned to 94b5d5ab91)