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
- Check/wait until nbf before using the token (client-side retry after the timestamp)
- Synchronize clocks with NTP on the verifying server
- Increase the leeway interval passed to StrictValidAt to absorb moderate skew
- 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
- Keep all servers NTP-synchronized
- Issue tokens with nbf = now (not future) unless delayed activation is intended
- Configure leeway proportional to your worst-case measured clock skew
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
- The token was issued in the future
- No constraint given.
- The claim " " is a registered claim, another constraint…
- You should pass a plain token
- The token does not have the claim
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)