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

  1. Set tokens to at least 1
  2. Remove the #[RateLimit] attribute if you meant to disable rate limiting
  3. Validate any config/env-derived token count before wiring it (max(1, $tokens))
  4. 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

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


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)