tymondesigns/jwt-auth · error · Tymon\JWTAuth\Exceptions\TokenInvalidException
Could not decode token: {exception message}
Error message
Could not decode token: {exception message} What it means
Thrown by the Lcobucci provider's decode() when lcobucci/jwt's parser cannot parse the input string into a Plain token; the parser's message is appended and chained. It means the string had a token-like shape but its segments are not valid base64url-encoded JSON. Note the package's TokenValidator usually rejects shape problems first, so reaching this error means there were 3 dot-separated segments whose content could not be parsed.
Source
Thrown at src/Providers/JWT/Lcobucci.php:113
throw new JWTException('Could not create token: '.$e->getMessage(), $e->getCode(), $e);
}
}
/**
* Decode a JSON Web Token.
*
* @param string $token
* @return array
*
* @throws \Tymon\JWTAuth\Exceptions\JWTException
*/
public function decode($token)
{
try {
/** @var \Lcobucci\JWT\Token\Plain */
$token = $this->config->parser()->parse($token);
} catch (Exception $e) {
throw new TokenInvalidException('Could not decode token: '.$e->getMessage(), $e->getCode(), $e);
}
if (! $this->config->validator()->validate($token, ...$this->config->validationConstraints())) {
throw new TokenInvalidException('Token Signature could not be verified.');
}
return Collection::wrap($token->claims()->all())
->map(function ($claim) {
if ($claim instanceof DateTimeInterface) {
return $claim->getTimestamp();
}
return is_object($claim) && method_exists($claim, 'getValue')
? $claim->getValue()
: $claim;
})
->toArray();
}View on GitHub (pinned to 6c70930a92)
Solutions
- Log the exact raw token received and compare it byte-for-byte with the token your issuer produced (often a transport/encoding mangling, not a JWT problem)
- Manually decode the segments to find the broken one: list($h, $p, $s) = explode('.', $token); json_decode(base64_decode(strtr($h, '-_', '+/'))); - whichever segment fails is the culprit
- Fix the client so the token is transmitted unchanged; if it came through a URL, make sure it is not urldecoded twice or re-encoded
- If the token is genuinely corrupt, have the client discard it and obtain a fresh one via login/refresh
Example fix
// before - sending a mangled token $response = $client->withHeaders(['Authorization' => 'Bearer ' . urlencode($token)])->get($url); // after - JWTs are URL-safe already; send unchanged $response = $client->withHeaders(['Authorization' => 'Bearer ' . $token])->get($url);
Defensive patterns
Strategy: validation
Validate before calling
// Cheap pre-check before handing the string to decode()
function isParsableJwt(string $token): bool
{
$parts = explode('.', $token);
if (count($parts) !== 3) {
return false;
}
foreach ([0, 1] as $i) {
$json = json_decode((string) base64_decode(strtr($parts[$i], '-_', '+/'), true), true);
if (!is_array($json)) {
return false;
}
}
return true;
} Type guard
function isParsableJwt(string $token): bool
{
$parts = explode('.', $token);
if (count($parts) !== 3) {
return false;
}
foreach ([0, 1] as $i) {
$decoded = base64_decode(strtr($parts[$i], '-_', '+/'), true);
if ($decoded === false || json_decode($decoded, true) === null) {
return false;
}
}
return true;
} Try / catch
use Tymon\JWTAuth\Exceptions\TokenInvalidException;
try {
$user = auth('api')->parseToken()->authenticate();
} catch (TokenInvalidException $e) {
return response()->json(['error' => 'token_invalid'], 401);
} Prevention
- Send tokens exactly as issued - never urlencode a JWT, it is already URL-safe base64
- Keep JWTs on a single line in storage, headers, and logs
- Pre-validate shape at your API boundary and reject early with 401 before touching the JWT stack
When it happens
Trigger: Calling JWTAuth::parseToken()->authenticate(), JWTAuth::decode(new Token($token)), or the middleware auth:api when the presented string has three segments but the header or payload is not valid base64url JSON, the signature segment contains non-base64url characters, or arbitrary junk like 'undefined.eyJ9.x' / an empty signature body is sent.
Common situations: Token mangled by URL encoding/decoding in transit (base64url - and _ converted), double-encoded base64, tokens copied with truncation or inserted line breaks, tokens produced by another library with a non-standard encoding, or a client concatenating strings into the Authorization header.
Related errors
- Could not create token: {exception message}
- Token Signature could not be verified.
- The given algorithm could not be found
- Private key is not set.
- Secret is not set.
AI-assisted analysis of tymondesigns/jwt-auth@6c70930a92 (2026-08-21).
Data as JSON: /api/errors/2546e3c97f818f6e.
Report an issue: GitHub.