Exceptions
RoundlyConsulting\Jwt\Jose\Exceptions\JwtException extends RuntimeException and is the base of every token error. Catch it to handle any failure uniformly, or a subclass for a precise cause:
| Exception | Thrown when |
|---|---|
JwtException | Base class — catch it to handle any token failure. |
MalformedToken | Not three segments, invalid base64url, a header or payload that isn’t a JSON object, or a token over 8 KB. |
AlgorithmMismatch | The header alg isn’t the pinned algorithm, or the key type doesn’t match it — blocks alg:none and RS256↔HS256 confusion. |
InvalidSignature | The RS256 or HS256 signature fails; also every per-issuer service-token iss absent from the secret map, even an allow-listed one. |
UnexpectedTokenType | A typ header is present and isn’t JWT (RFC 8725 explicit typing). |
UnencodableClaims | A claim can’t be serialised to JSON, e.g. non-UTF-8 bytes in an extra claim. |
TokenExpired | exp, minus leeway, is in the past. |
TokenNotYetValid | nbf or iat is in the future beyond the leeway. |
UnexpectedCriticalHeader | The token carries a crit header. |
ClaimMismatch | A required claim is absent or mistyped; an iss/aud pin mismatch; service scope, audience or issuer checks. |
KeyLoadFailed | A key is missing, unreadable (the file exists but the PHP process can’t read it), not RSA, under 2048 bits, or its path isn’t configured. |
EmptySecret | An HMAC secret is empty. |
PemAsHmacSecret | A PEM-encoded key was passed as an HMAC secret. |
WeakSecret | An HMAC secret is under 32 bytes, a single repeated byte, or carries raw DER key material. |
use RoundlyConsulting\Jwt\Facades\Jwt;
use RoundlyConsulting\Jwt\Jose\Exceptions\JwtException;
use RoundlyConsulting\Jwt\Jose\Exceptions\TokenExpired;
try {
$claims = Jwt::verify($jwt);
} catch (TokenExpired $e) {
// Prompt a re-login / refresh flow.
} catch (JwtException $e) {
// Any other token failure: 401.
}Misconfiguration is a 500, not a 401
Both guards turn a JwtException into a null user — a 401. Operator errors must never take that path, so they are deliberately not JwtExceptions:
- RoundlyConsulting\Jwt\Exceptions\JwtMisconfigured — an issuer or audience that is not set (null or blank), a guard name that isn’t a jwt guard, an uncallable token_version, a guard mintAccessToken() whose request names another audience, or a config value of the wrong shape — a TTL of five, an unparseable switch, a malformed service.secrets pair or an issuer it names twice, an identity that isn’t a ClaimsAuthenticatable. It never prints a secret: a repeated issuer is named only when it looks like one, otherwise by the entries’ positions, and for service.secret / service.secrets the message gives only the value’s type (“must be a string or null, array given”), while the value never rides a trace frame.
- RoundlyConsulting\Jwt\ServiceTokens\Exceptions\ServiceAuthMisconfigured — a missing or weak service secret, no resolvable service name, no audience or own issuer on issue, a missing own entry in the per-issuer map, or reserved extra claims.
- KeyLoadFailed is a JwtException, but the jwt guard rethrows it ahead of the generic handler — a missing or invalid RSA key is an operator error, not an unauthenticated caller.
KeyLoadFailed messages are actionable: they name the missing or unreadable path and the fix, such as running jwt:generate-keys, setting the env var or granting the PHP user read access.
Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.