{"record":{"id":"e3887007aa5918b2","repo":"lcobucci/jwt","slug":"you-should-pass-a-plain-token","errorCode":null,"errorMessage":"You should pass a plain token","messagePattern":"You should pass a plain token","errorType":"exception","errorClass":"Lcobucci\\JWT\\Validation\\ConstraintViolation","httpStatus":null,"severity":"error","filePath":"src/Validation/Constraint/HasClaim.php","lineNumber":26,"sourceCode":"use Lcobucci\\JWT\\Validation\\Constraint;\nuse Lcobucci\\JWT\\Validation\\ConstraintViolation;\n\nuse function in_array;\n\nfinal readonly class HasClaim implements Constraint\n{\n    /** @param non-empty-string $claim */\n    public function __construct(private string $claim)\n    {\n        if (in_array($claim, Token\\RegisteredClaims::ALL, true)) {\n            throw CannotValidateARegisteredClaim::create($claim);\n        }\n    }\n\n    public function assert(Token $token): void\n    {\n        if (! $token instanceof UnencryptedToken) {\n            throw ConstraintViolation::error('You should pass a plain token', $this);\n        }\n\n        $claims = $token->claims();\n\n        if (! $claims->has($this->claim)) {\n            throw ConstraintViolation::error('The token does not have the claim \"' . $this->claim . '\"', $this);\n        }\n    }\n}\n","sourceCodeStart":8,"sourceCodeEnd":36,"githubUrl":"https://github.com/lcobucci/jwt/blob/375813049c24c7111bda8b6884c57b071ceb2fe7/src/Validation/Constraint/HasClaim.php#L8-L36","documentation":"HasClaim can only inspect the claim set of a plain (unencrypted) JWT. If assert() is handed a Token that is not an UnencryptedToken — e.g. a signed/encrypted token object that doesn't expose claims — it raises ConstraintViolation 'You should pass a plain token'. This guards against attempting claim inspection on opaque tokens.","triggerScenarios":"Calling (new HasClaim('custom'))->assert($token) where $token is not an UnencryptedToken, e.g. when a Validator is run against an encrypted JWE token or a Token subclass without claim access.","commonSituations":"Running a shared validator configuration over both JWS and JWE tokens; passing the wrong token variable into assert(); pipelines where decryption step was skipped.","solutions":["Only run this constraint on decrypted/plain tokens; decrypt or convert the token first","Check $token instanceof UnencryptedToken before adding the constraint to a validator","Split validation into two paths: one for encrypted tokens, one for plain tokens"],"exampleFix":"// before\n(new Validator())->assert($someEncryptedToken, new HasClaim('role'));\n// after\nif ($token instanceof UnencryptedToken) {\n    (new Validator())->assert($token, new HasClaim('role'));\n}","handlingStrategy":"type-guard","validationCode":"if (!$token instanceof \\Jose\\Component\\Core\\UnencryptedToken) {\n    throw new \\LogicException('Claim constraints require a plain (unencrypted) token');\n}","typeGuard":"function isPlainToken(\\Jose\\Component\\Core\\TokenInterface $t): bool {\n    return $t instanceof \\Jose\\Component\\Core\\UnencryptedToken;\n}","tryCatchPattern":"try {\n    $validator->assert($token, new HasClaim('role'));\n} catch (RequiredConstraintsViolated|ConstraintViolationException $e) {\n    // includes non-plain-token violations; handle\n}","preventionTips":["Decrypt tokens before running claim constraints","Keep separate validators for JWE and JWS flows","Type-check tokens before adding constraints"],"tags":["jwt","validation","constraint","type-mismatch"],"backgroundTag":"type-mismatch","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"}