lcobucci/jwt · error · ConstraintViolation
The token is expired
Error message
The token is expired
What it means
This ConstraintViolation is thrown by the LooseValidAt constraint when the token's `exp` (expiration) claim is present and in the past relative to the current time (adjusted by the configured leeway). It is the library's way of enforcing that tokens are only accepted within their validity window.
Solutions
- Obtain a fresh token by re-authenticating/refreshing before validating again.
- Add leeway to absorb clock skew: new LooseValidAt($clock, DateInterval::createFromDateString('60 seconds')).
- Check the token's `exp` claim ($token->claims()->get('exp')) against your server time to confirm it really is past, not a clock issue.
- If clocks are the problem, synchronize both systems with NTP; extend the token lifetime at issuance if sessions legitimately need to be longer.
Example fix
// before: strict validation rejects skewed clocks
$validator->assert($token, [new LooseValidAt($clock)]); // expired thrown
// after: allow 1 minute of leeway
$validator->assert($token, [
new LooseValidAt($clock, DateInterval::createFromDateString('1 minute')),
]); Defensive patterns
Strategy: try-catch
Validate before calling
$exp = $token->claims()->get('exp');
if ($exp instanceof DateTimeInterface && $exp->getTimestamp() < time()) {
// refresh token before validating
} Type guard
function isUsableLifetime(Lcobucci\JWT\Token $token, int $leewaySeconds = 0): bool
{
$exp = $token->claims()->get('exp');
return ! $exp instanceof DateTimeInterface
|| $exp->getTimestamp() >= time() - $leewaySeconds;
} Try / catch
try {
$validator->assert($token, $constraints);
} catch (Lcobucci\JWT\Validation\ConstraintViolation $e) {
if ($e->getConstraint() instanceof Lcobucci\JWT\Validation\Constraint\LooseValidAt) {
// expired (or not-yet-valid): trigger token refresh / re-login
}
} Prevention
- NTP-sync all servers involved in issuing and validating tokens.
- Configure leeway proportional to your clock-skew tolerance (30-60s is typical).
- Implement a refresh-token flow so expired access tokens are replaced transparently.
- Check exp before expensive operations and refresh proactively shortly before expiry.
When it happens
Trigger: LooseValidAt::assert($token) calls assertExpiration(), which throws when Token::isExpired($now) returns true — i.e. the token carries an `exp` claim earlier than ($now - leeway).
Common situations: A user presenting a cached/stored token after its lifetime ended; long-running jobs replaying a token issued at start; server clock skewed far forward (or the issuer's clock far behind) so tokens appear expired; missing leeway configuration between systems with unsynchronized clocks.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- The token is not identified with the expected ID
- The token was not issued by the given issuers
- The token cannot be used yet
- The token is expired
- No constraint given.
AI-assisted analysis of lcobucci/jwt@375813049c (2026-09-14).
Data as JSON: /api/errors/ec4ca85539efa3ae.
Report an issue: GitHub.
Appendix: source
Thrown at src/Validation/Constraint/LooseValidAt.php:48
}
return $leeway;
}
public function assert(Token $token): void
{
$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));
}
/** @throws ConstraintViolation */
private function assertExpiration(Token $token, DateTimeInterface $now): void
{
if ($token->isExpired($now)) {
throw ConstraintViolation::error('The token is expired', $this);
}
}
/** @throws ConstraintViolation */
private function assertMinimumTime(Token $token, DateTimeInterface $now): void
{
if (! $token->isMinimumTimeBefore($now)) {
throw ConstraintViolation::error('The token cannot be used yet', $this);
}
}
/** @throws ConstraintViolation */
private function assertIssueTime(Token $token, DateTimeInterface $now): void
{
if (! $token->hasBeenIssuedBefore($now)) {
throw ConstraintViolation::error('The token was issued in the future', $this);
}
}View on GitHub (pinned to 375813049c)