{"record":{"id":"ec4ca85539efa3ae","repo":"lcobucci/jwt","slug":"the-token-is-expired","errorCode":null,"errorMessage":"The token is expired","messagePattern":"The token is expired","errorType":"validation","errorClass":"ConstraintViolation","httpStatus":null,"severity":"error","filePath":"src/Validation/Constraint/LooseValidAt.php","lineNumber":48,"sourceCode":"        }\n\n        return $leeway;\n    }\n\n    public function assert(Token $token): void\n    {\n        $now = $this->clock->now();\n\n        $this->assertIssueTime($token, $now->add($this->leeway));\n        $this->assertMinimumTime($token, $now->add($this->leeway));\n        $this->assertExpiration($token, $now->sub($this->leeway));\n    }\n\n    /** @throws ConstraintViolation */\n    private function assertExpiration(Token $token, DateTimeInterface $now): void\n    {\n        if ($token->isExpired($now)) {\n            throw ConstraintViolation::error('The token is expired', $this);\n        }\n    }\n\n    /** @throws ConstraintViolation */\n    private function assertMinimumTime(Token $token, DateTimeInterface $now): void\n    {\n        if (! $token->isMinimumTimeBefore($now)) {\n            throw ConstraintViolation::error('The token cannot be used yet', $this);\n        }\n    }\n\n    /** @throws ConstraintViolation */\n    private function assertIssueTime(Token $token, DateTimeInterface $now): void\n    {\n        if (! $token->hasBeenIssuedBefore($now)) {\n            throw ConstraintViolation::error('The token was issued in the future', $this);\n        }\n    }","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/lcobucci/jwt/blob/375813049c24c7111bda8b6884c57b071ceb2fe7/src/Validation/Constraint/LooseValidAt.php#L30-L66","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before: strict validation rejects skewed clocks\n$validator->assert($token, [new LooseValidAt($clock)]); // expired thrown\n\n// after: allow 1 minute of leeway\n$validator->assert($token, [\n    new LooseValidAt($clock, DateInterval::createFromDateString('1 minute')),\n]);","handlingStrategy":"try-catch","validationCode":"$exp = $token->claims()->get('exp');\nif ($exp instanceof DateTimeInterface && $exp->getTimestamp() < time()) {\n    // refresh token before validating\n}","typeGuard":"function isUsableLifetime(Lcobucci\\JWT\\Token $token, int $leewaySeconds = 0): bool\n{\n    $exp = $token->claims()->get('exp');\n    return ! $exp instanceof DateTimeInterface\n        || $exp->getTimestamp() >= time() - $leewaySeconds;\n}","tryCatchPattern":"try {\n    $validator->assert($token, $constraints);\n} catch (Lcobucci\\JWT\\Validation\\ConstraintViolation $e) {\n    if ($e->getConstraint() instanceof Lcobucci\\JWT\\Validation\\Constraint\\LooseValidAt) {\n        // expired (or not-yet-valid): trigger token refresh / re-login\n    }\n}","preventionTips":["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."],"tags":["jwt","token-expired","validation-constraint"],"backgroundTag":"jwt-token-expired","analyzedSha":"375813049c24c7111bda8b6884c57b071ceb2fe7","analyzedAt":"2026-09-14T11:12:28.004Z","contentChangedAt":"2026-09-14T11:12:28.004Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}