Summary
wheels.middleware.RateLimiter does not validate windowSeconds > 0 at
construction. When the value is 0, the strategy math divides by it and the
caller sees a generic CFML "You cannot divide by zero" exception instead of a
framework-shaped configuration error.
Confirmed against current main (vendor/wheels/middleware/RateLimiter.cfc)
on BoxLang 1.5.
Reproduction
var rl = new wheels.middleware.RateLimiter(maxRequests = 1, windowSeconds = 0);
rl.handle(request = {cgi: {remote_addr: "192.0.2.1"}}, next = function(req) { return "ok"; });
Result for strategy="fixedWindow" (default) and strategy="tokenBucket":
Application :: You cannot divide by zero.
strategy="slidingWindow" happens to accept windowSeconds=0 because its
arithmetic shape doesn't divide by the window — but a window of zero seconds
is still nonsensical and lets every request through.
Negative values for windowSeconds and maxRequests silently produce
inert behavior rather than a config error.
Why this matters
A developer who fat-fingers the window value gets a JVM-flavored error with
no pointer back to their set(middleware=[...]) line. The framework should
fail at construction time with Wheels.RateLimiter.InvalidConfiguration and
a message naming the bad parameter — the same pattern already used for
strategy, storage, and proxyStrategy.
Proposed fix
In RateLimiter.init(), add the same validation idiom already used for the
string params:
windowSeconds <= 0 → throw Wheels.RateLimiter.InvalidConfiguration
maxRequests < 0 → throw Wheels.RateLimiter.InvalidConfiguration
maxRequests = 0 is legitimate (block everything, useful for kill-switching).
Plus a regression spec in
vendor/wheels/tests/specs/middleware/RateLimiterSpec.cfc.
Discovered while
Writing the "Skip the Plugin: Building a Rate-Limited API in Wheels 4.0"
blog post and probing edge cases the article should mention. Fix is small
and queued on the same branch (claude/wheels-blog-article-titles-6kPSk).
Summary
wheels.middleware.RateLimiterdoes not validatewindowSeconds > 0atconstruction. When the value is
0, the strategy math divides by it and thecaller sees a generic CFML "You cannot divide by zero" exception instead of a
framework-shaped configuration error.
Confirmed against current
main(vendor/wheels/middleware/RateLimiter.cfc)on BoxLang 1.5.
Reproduction
var rl = new wheels.middleware.RateLimiter(maxRequests = 1, windowSeconds = 0); rl.handle(request = {cgi: {remote_addr: "192.0.2.1"}}, next = function(req) { return "ok"; });Result for
strategy="fixedWindow"(default) andstrategy="tokenBucket":strategy="slidingWindow"happens to acceptwindowSeconds=0because itsarithmetic shape doesn't divide by the window — but a window of zero seconds
is still nonsensical and lets every request through.
Negative values for
windowSecondsandmaxRequestssilently produceinert behavior rather than a config error.
Why this matters
A developer who fat-fingers the window value gets a JVM-flavored error with
no pointer back to their
set(middleware=[...])line. The framework shouldfail at construction time with
Wheels.RateLimiter.InvalidConfigurationanda message naming the bad parameter — the same pattern already used for
strategy,storage, andproxyStrategy.Proposed fix
In
RateLimiter.init(), add the same validation idiom already used for thestring params:
windowSeconds <= 0→ throwWheels.RateLimiter.InvalidConfigurationmaxRequests < 0→ throwWheels.RateLimiter.InvalidConfigurationmaxRequests = 0is legitimate (block everything, useful for kill-switching).Plus a regression spec in
vendor/wheels/tests/specs/middleware/RateLimiterSpec.cfc.Discovered while
Writing the "Skip the Plugin: Building a Rate-Limited API in Wheels 4.0"
blog post and probing edge cases the article should mention. Fix is small
and queued on the same branch (
claude/wheels-blog-article-titles-6kPSk).