lcobucci/jwt · error · ConstraintViolation

The token was issued in the future

Error message

The token was issued in the future

What it means

This error comes from lcobucci/jwt's StrictValidAt validation constraint. When a token is validated with the constraint (via Validator::assert), it checks the 'iat' (Issued At) claim against the current clock using Token::hasBeenIssuedBefore(). If the iat timestamp is later than 'now' — even by one second — the library rejects the token because it could not have been issued yet, which usually means clock skew between the issuer and validator or a bad clock on the machine.

Solutions

  1. Synchronize clocks on both issuer and validator machines with NTP (e.g. enable timed/chrony, 'sudo timedatectl set-ntp true') so iat is never in the future relative to the validator.
  2. If small skew is unavoidable, allow leeway: pass a DateInterval to the constraint, e.g. new StrictValidAt(SystemClock::fromUTC(), new DateInterval('PT30S')), which tolerates tokens issued up to 30s in the future.
  3. Fix the token generation code that sets iat — ensure it uses the current time in seconds (new DateTimeImmutable('@' . time()) or simply new DateTimeImmutable('now')) and not milliseconds or a future date.
  4. Catch RequiredConstraintsViolated and reject/refresh the token instead of crashing, logging the iat vs now values to diagnose persistent skew.

Example fix

// before
$validator->assert($token, new StrictValidAt($clock)); // throws when iat is a few seconds ahead

// after — tolerate up to 30 seconds of clock skew
$validator->assert(
    $token,
    new StrictValidAt(SystemClock::fromUTC(), new DateInterval('PT30S'))
);
Defensive patterns

Strategy: try-catch

Validate before calling

// before asserting, sanity-check the iat claim yourself
$claims = $token->claims();
$leeway = 30;
if ($claims->has('iat')) {
    $iat = $claims->get('iat');
    $now = new DateTimeImmutable('now', new DateTimeZone('UTC'));
    if ($iat->getTimestamp() > $now->getTimestamp() + $leeway) {
        // token issued in the future: fix clocks or reject early
    }
}

Type guard

function hasFutureIat(Lcobucci\JWT\Token $token, int $leeway = 30): bool
{
    if (!$token->claims()->has('iat')) {
        return false;
    }
    $iat = $token->claims()->get('iat');
    return $iat->getTimestamp() > (new DateTimeImmutable())->getTimestamp() + $leeway;
}

Try / catch

use Lcobucci\JWT\Validation\RequiredConstraintsViolated;

try {
    $validator->assert($token, new StrictValidAt($clock, new DateInterval('PT30S')));
} catch (RequiredConstraintsViolated $e) {
    foreach ($e->getViolations() as $v) {
        if ($v->getMessage() === 'The token was issued in the future') {
            // handle clock skew: refresh token or resync NTP
        }
    }
}

Prevention

When it happens

Trigger: Running Validator->assert($token, new StrictValidAt($clock)) where the token's iat claim is a Unix timestamp greater than the clock's current time. Typical concrete calls: validating a JWT received from another server/service whose clock is a few seconds or minutes ahead, or a token minted with a hand-built iat set incorrectly in the future (e.g. using milliseconds instead of seconds, or a wrong timezone conversion).

Common situations: Distributed systems with unsynchronized NTP clocks between the token issuer (auth server) and validator (API server); local development where the machine clock drifted; test fixtures that hardcode iat timestamps in the future; microservices where the token was just generated and iat equals the remote clock's 'now' while the local clock lags behind.

Related errors


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

Appendix: source

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

    {
        if (! $token->claims()->has(Token\RegisteredClaims::NOT_BEFORE)) {
            throw ConstraintViolation::error('"Not Before" claim missing', $this);
        }

        if (! $token->isMinimumTimeBefore($now)) {
            throw ConstraintViolation::error('The token cannot be used yet', $this);
        }
    }

    /** @throws ConstraintViolation */
    private function assertIssueTime(UnencryptedToken $token, DateTimeInterface $now): void
    {
        if (! $token->claims()->has(Token\RegisteredClaims::ISSUED_AT)) {
            throw ConstraintViolation::error('"Issued At" claim missing', $this);
        }

        if (! $token->hasBeenIssuedBefore($now)) {
            throw ConstraintViolation::error('The token was issued in the future', $this);
        }
    }
}

View on GitHub (pinned to 375813049c)