{"record":{"id":"e8c512ac2abc7f7e","repo":"App-vNext/Polly","slug":"pertimespan-must-be-a-positive-timespan","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/AsyncRateLimitSyntax.cs","lineNumber":37,"sourceCode":"    /// </summary>\n    /// <param name=\"numberOfExecutions\">The number of executions (call it N) permitted per timespan.</param>\n    /// <param name=\"perTimeSpan\">How often N executions are permitted.</param>\n    /// <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    /// <returns>The policy instance.</returns>\n    public static AsyncRateLimitPolicy RateLimitAsync(\n        int numberOfExecutions,\n        TimeSpan perTimeSpan,\n        int maxBurst)\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 AsyncRateLimitPolicy(rateLimiter);\n    }","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/App-vNext/Polly/blob/d0e46bdb1ee11ea50d0e4b6846d2633d6bc09bac/src/Polly/RateLimit/AsyncRateLimitSyntax.cs#L19-L55","documentation":"Thrown by RateLimitAsync when perTimeSpan is less than or equal to TimeSpan.Zero. perTimeSpan defines the refill window for the token bucket; a non-positive window breaks the bucket math. The guard runs after the numberOfExecutions check.","triggerScenarios":"Calling Policy.RateLimitAsync(n, TimeSpan.Zero, burst) or with a negative TimeSpan. Also hit when perTimeSpan comes from a misconfigured duration (zero ticks) or a subtraction that underflows.","commonSituations":"Configuring perTimeSpan from a numeric setting interpreted with the wrong unit (e.g. ms vs s) producing zero; default(TimeSpan) passed inadvertently; environment-driven window that is unset.","solutions":["Provide a positive TimeSpan, e.g. TimeSpan.FromSeconds(1).","Validate the config value: if (perTimeSpan <= TimeSpan.Zero) throw; before constructing the policy.","Confirm the unit conversion (ticks/ms/s) of the source setting.","Default optional config to a sane positive value rather than default(TimeSpan)."],"exampleFix":"// before\nvar p = Policy.RateLimitAsync(10, TimeSpan.Zero, 10);\n\n// after\nvar p = Policy.RateLimitAsync(10, TimeSpan.FromSeconds(1), 10);","handlingStrategy":"validation","validationCode":"if (perTimeSpan <= TimeSpan.Zero) throw new ArgumentOutOfRangeException(nameof(perTimeSpan));\nvar p = Policy.RateLimitAsync(n, perTimeSpan, maxBurst);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Confirm the unit (ticks/ms/s) of the source config value.","Default optional perTimeSpan to a known positive value, not default(TimeSpan).","Validate the full config triple at startup."],"tags":["argumentoutofrange","ratelimit","config","validation","timespan","legacy-api"],"backgroundTag":null,"analyzedSha":"d0e46bdb1ee11ea50d0e4b6846d2633d6bc09bac","analyzedAt":"2026-08-13T16:36:01.959Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}