lcobucci/jwt · error · Lcobucci\JWT\Token\InvalidTokenStructure
must be an array with non-empty-string keys
Error message
{part} must be an array with non-empty-string keys What it means
The JWT parser requires header and claim arrays to have non-empty string keys, so an entry with an empty-string key ('') makes the token structurally invalid. It is thrown from guardAgainstEmptyStringKeys while parsing the header or claims of a token string. This prevents ambiguous, unnameable header parameters or claims from entering a Token object.
Solutions
- Fix the token issuer so it never emits claims or header parameters with empty-string names
- Regenerate the token from a trusted source and re-verify
- Validate token structure (or JSON-decode and check keys) before parsing if tokens come from untrusted input
Example fix
// before (token payload)
{"": "oops", "sub": "123"}
// after
{"sub": "123"} Defensive patterns
Strategy: validation
Validate before calling
$parts = json_decode($payloadJson, true);
foreach ($parts as $key => $v) {
if (!is_string($key) || $key === '') {
throw new \InvalidArgumentException('Token contains a claim/header with an empty name');
}
} Try / catch
try {
$token = $parser->parse($jwt);
} catch (InvalidTokenStructure $e) {
// reject token as structurally invalid
} Prevention
- Only accept tokens from issuers you control or trust
- Treat InvalidTokenStructure on parse as an authentication failure
- Sanity-check raw JSON payloads in tests with malformed fixtures
When it happens
Trigger: Calling $parser->parse($tokenString) where the decoded token payload JSON contains an object member whose key is the empty string (e.g. {"": 1}) in either the header or the claims set.
Common situations: Hand-crafted or third-party-minted tokens with malformed JSON objects; misconfigured token issuers; corrupted/modified token strings; passing raw JSON blobs that are not real JWTs to the parser.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Error while decoding from Base64Url, invalid base64…
- Builder#withClaim() is meant to be used for non-registered…
- The JWT string is missing the Header part
- The JWT string is missing the Claim part
- The JWT string is missing the Signature part
AI-assisted analysis of lcobucci/jwt@375813049c (2026-09-14).
Data as JSON: /api/errors/2e4ce04a1179cef3.
Report an issue: GitHub.
Appendix: source
Thrown at src/Token/Parser.php:146
}
$claims[$claim] = $this->convertDate($claims[$claim]);
}
return $claims;
}
/**
* @param array<string, mixed> $array
* @param non-empty-string $part
*
* @phpstan-assert array<non-empty-string, mixed> $array
*/
private function guardAgainstEmptyStringKeys(array $array, string $part): void
{
foreach ($array as $key => $value) {
if ($key === '') {
throw InvalidTokenStructure::arrayExpected($part);
}
}
}
/** @throws InvalidTokenStructure */
private function convertDate(int|float|string $timestamp): DateTimeImmutable
{
if (! is_numeric($timestamp)) {
throw InvalidTokenStructure::dateIsNotParseable($timestamp);
}
$normalizedTimestamp = number_format((float) $timestamp, self::MICROSECOND_PRECISION, '.', '');
$date = DateTimeImmutable::createFromFormat('U.u', $normalizedTimestamp);
if ($date === false) {
throw InvalidTokenStructure::dateIsNotParseable($normalizedTimestamp);
}View on GitHub (pinned to 375813049c)