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

  1. Fix the token issuer so it never emits claims or header parameters with empty-string names
  2. Regenerate the token from a trusted source and re-verify
  3. 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

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


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)