symfony/http-kernel · error · InvalidArgumentException
The "$tokens" argument of
Error message
The "$tokens" argument of "%s" must be greater than 0, "%d" given.
What it means
The #[RateLimit] attribute validates its tokens argument at construction; the limiter must consume at least 1 token, so values < 1 throw InvalidArgumentException immediately when the attribute is instantiated. This is fail-fast validation of the attribute's parameters.
Solutions
- Set tokens to at least 1
- Remove the #[RateLimit] attribute if you meant to disable rate limiting
- Validate any config/env-derived token count before wiring it (max(1, $tokens))
- Check for arithmetic that can produce 0 (e.g. intdiv, floor)
Example fix
// before
#[RateLimit(tokens: 0)]
public function index() {...}
// after
#[RateLimit(tokens: 1)]
public function index() {...} Defensive patterns
Strategy: validation
Validate before calling
// resolve tokens from config safely before wiring the attribute $tokens = max(1, (int) ($config['rate_limit_tokens'] ?? 1));
Try / catch
try { $attr = new \Symfony\Component\HttpKernel\Attribute\RateLimit(tokens: $tokens); } catch (\InvalidArgumentException $e) { $attr = new \Symfony\Component\HttpKernel\Attribute\RateLimit(tokens: 1); } Prevention
- Never set tokens: 0 to 'disable' limiting — remove the attribute instead
- Clamp config-derived values with max(1, $tokens)
- Add unit tests that instantiate attributes with production config values
- Watch for integer division that can yield 0
When it happens
Trigger: Declaring #[RateLimit(tokens: 0)] or a negative value, or passing a non-literal (e.g. from config) that evaluates to 0 when PHP resolves the attribute on the controller.
Common situations: Copying an attribute example and setting tokens: 0 to 'disable' limiting (should remove the attribute instead); computing tokens from an env/config value that defaults to 0; integer division yielding 0.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- Invalid log level " ".
- Rate limiter " " does not exist. Did you forget to…
- The log level " " does not exist.
- You can only pin one resolver per argument, but argument "$
- #[MapQueryParameter] cannot be used on controller argument
AI-assisted analysis of symfony/http-kernel@aa3a39d728 (2026-09-13).
Data as JSON: /api/errors/ba282984ed294faa.
Report an issue: GitHub.
Appendix: source
Thrown at Attribute/RateLimit.php:48
/** @var string[] */
public readonly array $methods;
/**
* @param string $limiter The configured limiter name
* @param string|Expression|\Closure|null $key A literal string key, an Expression, or a Closure (defaults to client IP + method + path)
* @param int $tokens The number of tokens to consume
* @param string[]|string $methods HTTP methods to rate limit; empty means all methods
* @param bool $exposeHeaders Whether this limiter's state may be exposed via the `X-RateLimit-*` response headers, opt-in
*/
public function __construct(
public readonly string $limiter,
public readonly string|Expression|\Closure|null $key = null,
public readonly int $tokens = 1,
array|string $methods = [],
public readonly bool $exposeHeaders = false,
) {
if ($this->tokens < 1) {
throw new \InvalidArgumentException(\sprintf('The "$tokens" argument of "%s" must be greater than 0, "%d" given.', self::class, $this->tokens));
}
if (\in_array('GET', $methods = array_map('strtoupper', (array) $methods), true)) {
$methods[] = 'HEAD';
}
$this->methods = $methods;
}
}
View on GitHub (pinned to aa3a39d728)