lcobucci/jwt · error · Lcobucci\JWT\Token\InvalidTokenStructure
The JWT string must have two dots
Error message
The JWT string must have two dots
What it means
A JWT must contain exactly three dot-separated parts. Parser::splitJwt() explodes the string on '.' and throws InvalidTokenStructure::missingOrNotEnoughSeparators() when the resulting array does not have exactly 3 elements, i.e. the string must contain precisely two dots.
Solutions
- Verify you pass only the JWT itself, not the full 'Bearer xxx' header — strip the scheme prefix
- Count the dots before parsing: substr_count($jwt, '.') === 2
- If you received a 5-segment string, it is a JWE — this parser does not support encrypted tokens
- Catch InvalidTokenStructure around parse() and return a clear client error
Example fix
// before
$token = $parser->parse($request->getHeader('Authorization')[0]);
// after
$jwt = str_replace('Bearer ', '', $request->getHeader('Authorization')[0]);
if (substr_count($jwt, '.') !== 2) {
throw new InvalidArgumentException('Not a compact JWT');
}
$token = $parser->parse($jwt); Defensive patterns
Strategy: validation
Validate before calling
if (substr_count($jwt, '.') !== 2) { throw new InvalidArgumentException('Not a compact JWS (needs exactly two dots)'); } Type guard
function isCompactJwt(string $s): bool { return substr_count($s, '.') === 2; } Try / catch
try { $token = $parser->parse($jwt); } catch (Lcobucci\JWT\InvalidTokenStructure $e) { return error_401('Malformed token'); } Prevention
- Strip the 'Bearer ' scheme before parsing
- Recognize 5-dot strings as JWE and route them to a JWE-capable library
- Validate token shape centrally before any parser call
When it happens
Trigger: Passing a string with fewer or more than two dots: 'header.claims' (unsigned), 'header.claims.sig.extra' (JWE or nested token), a whole Authorization header ('Bearer eyJ...' contains no dot issue but a JWE has 5 dots), or passing a non-JWT string.
Common situations: Accidentally passing an access token in JWE format; passing the raw Authorization header value including 'Bearer '; concatenating token + extra data; passing an opaque session token instead of a JWT.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- The JWT string is missing the Header part
- The JWT string is missing the Claim part
- The JWT string is missing the Signature part
- headers must be an array with non-empty-string keys
- claims must be an array with non-empty-string keys
AI-assisted analysis of lcobucci/jwt@375813049c (2026-09-14).
Data as JSON: /api/errors/e480a04e315124e9.
Report an issue: GitHub.
Appendix: source
Thrown at src/Token/Parser.php:65
$this->parseSignature($encodedSignature),
);
}
/**
* Splits the JWT string into an array
*
* @param non-empty-string $jwt
*
* @return string[]
*
* @throws InvalidTokenStructure When JWT doesn't have all parts.
*/
private function splitJwt(string $jwt): array
{
$data = explode('.', $jwt);
if (count($data) !== 3) {
throw InvalidTokenStructure::missingOrNotEnoughSeparators();
}
return $data;
}
/**
* Parses the header from a string
*
* @param non-empty-string $data
*
* @return array<non-empty-string, mixed>
*
* @throws UnsupportedHeaderFound When an invalid header is informed.
* @throws InvalidTokenStructure When parsed content isn't an array.
*/
private function parseHeader(string $data): array
{
$header = $this->decoder->jsonDecode($this->decoder->base64UrlDecode($data));View on GitHub (pinned to 375813049c)