{"record":{"id":"9ec073dcde51805c","repo":"dotnet/reactive","slug":"period","errorCode":null,"errorMessage":"period","messagePattern":"period","errorType":"exception","errorClass":"ArgumentOutOfRangeException","httpStatus":null,"severity":"error","filePath":"AsyncRx.NET/System.Reactive.Async/Linq/Operators/Interval.cs","lineNumber":15,"sourceCode":"﻿// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT License.\n// See the LICENSE file in the project root for more information. \n\nusing System.Reactive.Concurrency;\nusing System.Threading.Tasks;\n\nnamespace System.Reactive.Linq\n{\n    public partial class AsyncObservable\n    {\n        public static IAsyncObservable<long> Interval(TimeSpan period)\n        {\n            if (period < TimeSpan.Zero)\n                throw new ArgumentOutOfRangeException(nameof(period));\n\n            return Create<long>(observer => AsyncObserver.Interval(observer, period));\n        }\n\n        public static IAsyncObservable<long> Interval(TimeSpan period, IAsyncScheduler scheduler)\n        {\n            if (period < TimeSpan.Zero)\n                throw new ArgumentOutOfRangeException(nameof(period));\n            if (scheduler == null)\n                throw new ArgumentNullException(nameof(scheduler));\n\n            return Create<long>(observer => AsyncObserver.Interval(observer, period, scheduler));\n        }\n    }\n\n    public partial class AsyncObserver\n    {\n        public static ValueTask<IAsyncDisposable> Interval(IAsyncObserver<long> observer, TimeSpan period) => Timer(observer, period, period);","sourceCodeStart":1,"sourceCodeEnd":33,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/AsyncRx.NET/System.Reactive.Async/Linq/Operators/Interval.cs#L1-L33","documentation":"Interval validates that the period is non-negative; a negative TimeSpan has no valid timing meaning, so ArgumentOutOfRangeException fires before the timer-based observable is created. Fix: pass TimeSpan.Zero or a positive duration.","triggerScenarios":"Calling AsyncObservable.Interval(TimeSpan.FromMilliseconds(-1)) or any negative TimeSpan, often from a computed or parsed interval value that ended up negative (e.g. subtracting timestamps).","commonSituations":"Config values parsed into TimeSpan with wrong sign, diffing two DateTimes in the wrong order, or default(TimeSpan) mistakes combined with arithmetic producing negative durations.","solutions":["Ensure the period is >= TimeSpan.Zero; clamp with Math.Max(TimeSpan.Zero, period).","Fix the computation producing the negative TimeSpan (check subtraction order of DateTime/Stopwatch values).","Use TimeSpan.Zero or a positive duration for 'emit as soon as scheduled' semantics if that was intended."],"exampleFix":"// before\nvar period = end - start; // may be negative\nvar xs = AsyncObservable.Interval(period);\n// after\nvar period = end > start ? end - start : TimeSpan.Zero;\nvar xs = AsyncObservable.Interval(period);","handlingStrategy":"validation","validationCode":"if (period < TimeSpan.Zero) throw new ArgumentException(\"period must be >= TimeSpan.Zero\", nameof(period));\nvar safePeriod = TimeSpan.FromTicks(Math.Max(0, period.Ticks));","typeGuard":"static bool IsValidPeriod(TimeSpan p) => p >= TimeSpan.Zero;","tryCatchPattern":"try { var xs = AsyncObservable.Interval(period); }\ncatch (ArgumentOutOfRangeException ex) when (ex.ParamName == \"period\") { /* clamp and retry */ }","preventionTips":["Clamp computed durations with Math.Max(TimeSpan.Zero, value)","Check subtraction order when deriving durations from timestamps","Validate TimeSpan config values on load"],"tags":["argument-out-of-range","scheduling","reactive"],"backgroundTag":"argument-out-of-range","analyzedSha":"94b5d5ab912789f5abe9a72138a25bbd716fe59c","analyzedAt":"2026-09-15T02:26:24.759Z","contentChangedAt":"2026-09-15T02:26:24.759Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}