{"record":{"id":"6940f19eff2c6243","repo":"lcobucci/jwt","slug":"this-constraint-was-only-usable-until-rfc3339-date","errorCode":null,"errorMessage":"This constraint was only usable until <RFC3339 date>","messagePattern":"This constraint was only usable until <RFC3339 date>","errorType":"validation","errorClass":"ConstraintViolation","httpStatus":null,"severity":"error","filePath":"src/Validation/Constraint/SignedWithUntilDate.php","lineNumber":38,"sourceCode":"        Signer $signer,\n        Signer\\Key $key,\n        private DateTimeImmutable $validUntil,\n        ?ClockInterface $clock = null,\n    ) {\n        $this->verifySignature = new SignedWith($signer, $key);\n\n        $this->clock = $clock ?? new class () implements ClockInterface {\n            public function now(): DateTimeImmutable\n            {\n                return new DateTimeImmutable();\n            }\n        };\n    }\n\n    public function assert(Token $token): void\n    {\n        if ($this->validUntil < $this->clock->now()) {\n            throw ConstraintViolation::error(\n                'This constraint was only usable until '\n                . $this->validUntil->format(DateTimeInterface::RFC3339),\n                $this,\n            );\n        }\n\n        $this->verifySignature->assert($token);\n    }\n}\n","sourceCodeStart":20,"sourceCodeEnd":48,"githubUrl":"https://github.com/lcobucci/jwt/blob/375813049c24c7111bda8b6884c57b071ceb2fe7/src/Validation/Constraint/SignedWithUntilDate.php#L20-L48","documentation":"SignedWithUntilDate is a decorator constraint that enforces a usability deadline: it holds a validUntil date and a clock, and before delegating it checks whether the deadline has passed. If the configured date is in the past, it throws with the RFC3339 date it stopped being usable. This lets you retire old signing keys/constraints on a schedule.","triggerScenarios":"Using a SignedWithUntilDate constraint whose validUntil date is earlier than clock->now() — calling Validator::assert($token, new SignedWithUntilDate(...)) after the configured expiry, regardless of whether the token signature itself is valid.","commonSituations":"Long-lived deployments where a temporary key-rotation constraint expired but code was not updated; clock skew or misconfigured system time making 'now' appear past the deadline; intentionally sunsetting a token set but still receiving old tokens from cached clients.","solutions":["Update the validUntil date (or remove the decorator) if the constraint should still be usable","Verify the system clock / Clock implementation is correct and not skewed","Reissue tokens under a constraint set that is still within its validity window","If this expiry was intentional, treat it as expected behavior and reject/refresh tokens client-side"],"exampleFix":"// before\nnew SignedWithUntilDate(new SignedWith(new Sha256(), $key), new DateTimeImmutable('2024-01-01 00:00:00'));\n// after\nnew SignedWithUntilDate(new SignedWith(new Sha256(), $key), new DateTimeImmutable('2026-12-31 23:59:59'));","handlingStrategy":"validation","validationCode":"if ($constraintValidUntil < (new DateTimeImmutable())) {\n    throw new RuntimeException('SignedWithUntilDate expired; update constraint configuration');\n}","typeGuard":"null","tryCatchPattern":"try {\n    $validator->assert($token, $untilDateConstraint);\n} catch (ConstraintViolation $e) {\n    // swap to current constraint set / trigger key rotation procedure\n}","preventionTips":["Tie constraint validity dates to deployment/rotation automation so they are renewed","Monitor upcoming expiry of key constraints and alert before they lapse","Verify server clocks are NTP-synced"],"tags":["jwt","validation","expired-constraint","clock"],"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"}