lcobucci/jwt · error · LeewayCannotBeNegative

Leeway cannot be negative

Error message

Leeway cannot be negative

What it means

StrictValidAt validates a token's iat/nbf/exp claims strictly against the current time, allowing an optional DateInterval leeway for clock skew. In its constructor, guardLeeway rejects a leeway interval created with invert=1 (a negative interval) because negative leeway is meaningless — the exception is LeewayCannotBeNegative. Leeway must be zero or a positive duration.

Solutions

  1. Use a positive DateInterval, e.g. new DateInterval('PT10M') for 10 minutes of leeway
  2. Check the source string for a leading minus sign when using DateInterval::createFromDateString()
  3. Pass null to use the default zero leeway if clock skew is not a concern

Example fix

// before
new StrictValidAt($clock, DateInterval::createFromDateString('-10 minutes'));
// after
new StrictValidAt($clock, DateInterval::createFromDateString('10 minutes'));
Defensive patterns

Strategy: validation

Validate before calling

$leeway = DateInterval::createFromDateString('10 minutes');
if ($leeway->invert === 1) { throw new InvalidArgumentException('Leeway must not be negative'); }

Type guard

function isValidLeeway(?DateInterval $leeway): bool { return $leeway === null || $leeway->invert === 0; }

Try / catch

try {
    $constraint = new StrictValidAt($clock, $leeway);
} catch (LeewayCannotBeNegative $e) {
    $leeway = new DateInterval('PT0S'); // or correct config
}

Prevention

When it happens

Trigger: new StrictValidAt($clock, new DateInterval('PT-5M')) or any DateInterval constructed via createFromDateString() with a negative spec (e.g. '-5 minutes') — DateInterval::invert is 1 for negative intervals.

Common situations: Typo like '-10 minutes' instead of '10 minutes' when configuring clock skew tolerance; computing the leeway by subtracting dates (DateTimeImmutable diff never does this, but manual math can); copying config from a library where negative leeway means 'past-only'.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of lcobucci/jwt@375813049c (2026-09-14). Data as JSON: /api/errors/28c47eced3fc5b4c. Report an issue: GitHub.

Appendix: source

Thrown at src/Validation/Constraint/StrictValidAt.php:30

use Psr\Clock\ClockInterface as Clock;

final readonly class StrictValidAt implements ValidAtInterface
{
    private DateInterval $leeway;

    public function __construct(private Clock $clock, ?DateInterval $leeway = null)
    {
        $this->leeway = $this->guardLeeway($leeway);
    }

    private function guardLeeway(?DateInterval $leeway): DateInterval
    {
        if ($leeway === null) {
            return new DateInterval('PT0S');
        }

        if ($leeway->invert === 1) {
            throw LeewayCannotBeNegative::create();
        }

        return $leeway;
    }

    public function assert(Token $token): void
    {
        if (! $token instanceof UnencryptedToken) {
            throw ConstraintViolation::error('You should pass a plain token', $this);
        }

        $now = $this->clock->now();

        $this->assertIssueTime($token, $now->add($this->leeway));
        $this->assertMinimumTime($token, $now->add($this->leeway));
        $this->assertExpiration($token, $now->sub($this->leeway));
    }

View on GitHub (pinned to 375813049c)