{"record":{"id":"e341ab65d52438c7","repo":"App-vNext/Polly","slug":"pertimespan-must-be-a-positive-timespan-e341ab","errorCode":null,"errorMessage":"perTimeSpan must be a positive timespan.","messagePattern":"perTimeSpan must be a positive timespan\\.","errorType":"exception","errorClass":"ArgumentOutOfRangeException","httpStatus":null,"severity":"error","filePath":"src/Polly/RateLimit/RateLimitTResultSyntax.cs","lineNumber":73,"sourceCode":"    /// <param name=\"maxBurst\">The maximum number of executions that will be permitted in a single burst (for example if none have been executed for a while).\n    /// This equates to the bucket-capacity of a token-bucket implementation.</param>\n    /// <param name=\"retryAfterFactory\">An (optional) factory to use to express retry-after back to the caller, when an operation is rate-limited.\n    /// <remarks>If null, a <see cref=\"RateLimitRejectedException\"/> with property <see cref=\"RateLimitRejectedException.RetryAfter\"/> will be thrown to indicate rate-limiting.</remarks></param>\n    /// <returns>The policy instance.</returns>\n    public static RateLimitPolicy<TResult> RateLimit<TResult>(\n        int numberOfExecutions,\n        TimeSpan perTimeSpan,\n        int maxBurst,\n        Func<TimeSpan, Context, TResult>? retryAfterFactory)\n    {\n        if (numberOfExecutions < 1)\n        {\n            throw new ArgumentOutOfRangeException(nameof(numberOfExecutions), numberOfExecutions, $\"{nameof(numberOfExecutions)} per timespan must be an integer greater than or equal to 1.\");\n        }\n\n        if (perTimeSpan <= TimeSpan.Zero)\n        {\n            throw new ArgumentOutOfRangeException(nameof(perTimeSpan), perTimeSpan, $\"{nameof(perTimeSpan)} must be a positive timespan.\");\n        }\n\n        if (maxBurst < 1)\n        {\n            throw new ArgumentOutOfRangeException(nameof(maxBurst), maxBurst, $\"{nameof(maxBurst)} must be an integer greater than or equal to 1.\");\n        }\n\n        var onePer = TimeSpan.FromTicks(perTimeSpan.Ticks / numberOfExecutions);\n\n        if (onePer <= TimeSpan.Zero)\n        {\n            throw new ArgumentOutOfRangeException(nameof(perTimeSpan), perTimeSpan, \"The number of executions per timespan must be positive.\");\n        }\n\n        IRateLimiter rateLimiter = new LockFreeTokenBucketRateLimiter(onePer, maxBurst);\n\n        return new RateLimitPolicy<TResult>(rateLimiter, retryAfterFactory);\n    }","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/App-vNext/Polly/blob/d0e46bdb1ee11ea50d0e4b6846d2633d6bc09bac/src/Polly/RateLimit/RateLimitTResultSyntax.cs#L55-L91","documentation":"Thrown by RateLimitTResultSyntax.RateLimit<TResult> when perTimeSpan is less than or equal to TimeSpan.Zero. The generic TResult overload requires a positive counting window, identical to the non-generic version.","triggerScenarios":"Calling Policy.RateLimit<TResult>(n, TimeSpan.Zero, ...) or a negative window on the generic sync overload.","commonSituations":"Config-driven window set to 0 from a missing key or wrong unit; env override yielding zero.","solutions":["Set perTimeSpan to a positive TimeSpan.","Enforce perTimeSpan > TimeSpan.Zero at config load.","Verify the time unit (seconds vs milliseconds) is not collapsing to zero."],"exampleFix":"// before\nvar policy = Policy.RateLimit<MyResult>(5, TimeSpan.Zero, 5, factory);\n// after\nvar policy = Policy.RateLimit<MyResult>(5, TimeSpan.FromSeconds(10), 5, factory);","handlingStrategy":"validation","validationCode":"var perTimeSpan = config.GetValue<TimeSpan>(\"RateLimit:Window\");\nif (perTimeSpan <= TimeSpan.Zero) throw new InvalidOperationException(\"RateLimit:Window must be positive.\");\nvar policy = Policy.RateLimit<TResult>(numberOfExecutions, perTimeSpan, maxBurst, retryAfterFactory);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Bind window config to TimeSpan and validate positivity at load.","Verify the unit string in config matches expectations.","Use options-pattern Validate() to surface bad config at startup."],"tags":["rate-limit","argumentoutofrange","timespan","generic","configuration"],"backgroundTag":null,"analyzedSha":"d0e46bdb1ee11ea50d0e4b6846d2633d6bc09bac","analyzedAt":"2026-08-13T16:36:01.959Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}