lcobucci/jwt · error · ConstraintViolation

The token cannot be used yet

Error message

The token cannot be used yet

What it means

When the 'nbf' claim is present, StrictValidAt checks $token->isMinimumTimeBefore($now->add(leeway)); if the current time is still before nbf, the token is not yet usable and it throws 'The token cannot be used yet'. This rejects tokens used ahead of their Not Before window.

Solutions

  1. Check/wait until nbf before using the token (client-side retry after the timestamp)
  2. Synchronize clocks with NTP on the verifying server
  3. Increase the leeway interval passed to StrictValidAt to absorb moderate skew
  4. Have the issuer stop setting a future nbf if tokens are meant to be immediately usable

Example fix

// before
new StrictValidAt($clock, new DateInterval('PT0S'));
// after (allow 30s skew)
new StrictValidAt($clock, new DateInterval('PT30S'));
Defensive patterns

Strategy: retry

Validate before calling

$nbf = $token->claims()->get('nbf');
if ($nbf instanceof DateTimeInterface && $nbf > (new DateTimeImmutable())->add($leeway)) {
    // schedule retry at $nbf instead of failing
}

Type guard

null

Try / catch

try {
    $validator->assert($token, new StrictValidAt($clock, $leeway));
} catch (ConstraintViolation $e) {
    if ($e->getMessage() === 'The token cannot be used yet') { /* delay and retry, or reject */ }
}

Prevention

When it happens

Trigger: Validating a token whose nbf timestamp is in the future relative to now + leeway — e.g. token issued for delayed activation, or verifier clock behind issuer clock by more than the leeway.

Common situations: Server clock skew: verifier's clock lags the issuer, so a just-issued token looks 'not yet valid'; issuer set nbf with an intentional future start time; containers/VMs with drifted clocks and no NTP; leeway configured too small.

Related errors


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

Appendix: source

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

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

        if ($token->isExpired($now)) {
            throw ConstraintViolation::error('The token is expired', $this);
        }
    }

    /** @throws ConstraintViolation */
    private function assertMinimumTime(UnencryptedToken $token, DateTimeInterface $now): void
    {
        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)