dotnet/reactive · error · ArgumentOutOfRangeException
Specified argument was out of the range of valid values…
Error message
Specified argument was out of the range of valid values. (Parameter 'period')
What it means
ControlScheduler.SchedulePeriodic requires a period of at least 1 millisecond because it is backed by a System.Windows.Forms.Timer, whose Interval property has the same lower bound (threshold taken from the WinForms Timer setter). Periods of TimeSpan.Zero, negative values, or sub-millisecond fractions throw ArgumentOutOfRangeException with Parameter 'period'.
Solutions
- Clamp the period to at least 1 ms: period = period < TimeSpan.FromMilliseconds(1) ? TimeSpan.FromMilliseconds(1) : period.
- If a tight/immediate loop is intended, use a different scheduler (Scheduler.Default / TaskPool) instead of ControlScheduler.
- Validate configured interval values at startup and fail fast with a clear message.
Example fix
// before scheduler.SchedulePeriodic(state, TimeSpan.FromMilliseconds(config.IntervalMs), tick); // 0 → throws // after var period = TimeSpan.FromMilliseconds(Math.Max(config.IntervalMs, 1)); scheduler.SchedulePeriodic(state, period, tick);
Defensive patterns
Strategy: validation
Validate before calling
var min = TimeSpan.FromMilliseconds(1); if (period < min) period = min; scheduler.SchedulePeriodic(state, period, action);
Type guard
static bool IsValidPeriod(TimeSpan p) => p.TotalMilliseconds >= 1;
Try / catch
try { scheduler.SchedulePeriodic(state, period, action); } catch (ArgumentOutOfRangeException ex) when (ex.ParamName == "period") { /* clamp to 1ms and retry */ } Prevention
- Clamp configured intervals to >= 1ms for WinForms Timer-based scheduling
- Remember ControlScheduler uses System.Windows.Forms.Timer (1ms floor)
- Use Scheduler.Default for sub-millisecond or zero periods
When it happens
Trigger: scheduler.SchedulePeriodic(state, TimeSpan.Zero, action), negative TimeSpan, or TimeSpan.FromTicks(<10000) / TimeSpan.FromMilliseconds(0.5) — anything with TotalMilliseconds < 1.
Common situations: Computing the interval from a config value of 0 interpreted as 'as fast as possible'; dividing a TimeSpan and rounding down to zero; porting code from another scheduler (e.g. DefaultScheduler) that accepts zero or negative periods as immediate/tight loops.
Related errors
- ArgumentOutOfRangeException
- Specified argument was out of the range of valid values…
- index
- minObservers
- throw new ArgumentOutOfRangeException(nameof(period));
AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15).
Data as JSON: /api/errors/0b445cfa5bbbe1b5.
Report an issue: GitHub.
Appendix: source
Thrown at Rx.NET/Source/src/System.Reactive/Platforms/Desktop/Concurrency/ControlScheduler.cs:164
/// <summary>
/// Schedules a periodic piece of work on the message loop associated with the control, using a Windows Forms Timer object.
/// </summary>
/// <typeparam name="TState">The type of the state passed to the scheduled action.</typeparam>
/// <param name="state">Initial state passed to the action upon the first iteration.</param>
/// <param name="period">Period for running the work periodically.</param>
/// <param name="action">Action to be executed, potentially updating the state.</param>
/// <returns>The disposable object used to cancel the scheduled recurring action (best effort).</returns>
/// <exception cref="ArgumentNullException"><paramref name="action"/> is null.</exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="period"/> is less than one millisecond.</exception>
public IDisposable SchedulePeriodic<TState>(TState state, TimeSpan period, Func<TState, TState> action)
{
//
// Threshold derived from Interval property setter in ndp\fx\src\winforms\managed\system\winforms\Timer.cs.
//
if (period.TotalMilliseconds < 1)
{
throw new ArgumentOutOfRangeException(nameof(period));
}
if (action == null)
{
throw new ArgumentNullException(nameof(action));
}
var createTimer = new Func<IScheduler, TState, IDisposable>((scheduler1, state1) =>
{
var timer = new System.Windows.Forms.Timer();
timer.Tick += (s, e) =>
{
if (!_control.IsDisposed)
{
state1 = action(state1);
}
};View on GitHub (pinned to 94b5d5ab91)