{"record":{"id":"d2cb30d43ff21628","repo":"lcobucci/jwt","slug":"headers-must-be-an-array-with-non-empty-string-keys","errorCode":null,"errorMessage":"headers must be an array with non-empty-string keys","messagePattern":"headers must be an array with non-empty-string keys","errorType":"exception","errorClass":"Lcobucci\\JWT\\Token\\InvalidTokenStructure","httpStatus":null,"severity":"error","filePath":"src/Token/Parser.php","lineNumber":86,"sourceCode":"        return $data;\n    }\n\n    /**\n     * Parses the header from a string\n     *\n     * @param non-empty-string $data\n     *\n     * @return array<non-empty-string, mixed>\n     *\n     * @throws UnsupportedHeaderFound When an invalid header is informed.\n     * @throws InvalidTokenStructure  When parsed content isn't an array.\n     */\n    private function parseHeader(string $data): array\n    {\n        $header = $this->decoder->jsonDecode($this->decoder->base64UrlDecode($data));\n\n        if (! is_array($header)) {\n            throw InvalidTokenStructure::arrayExpected('headers');\n        }\n\n        $this->guardAgainstEmptyStringKeys($header, 'headers');\n\n        if (array_key_exists('enc', $header)) {\n            throw UnsupportedHeaderFound::encryption();\n        }\n\n        if (! array_key_exists('typ', $header)) {\n            $header['typ'] = 'JWT';\n        }\n\n        return $header;\n    }\n\n    /**\n     * Parses the claim set from a string\n     *","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/lcobucci/jwt/blob/375813049c24c7111bda8b6884c57b071ceb2fe7/src/Token/Parser.php#L68-L104","documentation":"Parser::parseHeader() Base64Url-decodes and JSON-decodes the header segment, then requires the result to be an array. If it decodes to a scalar/string/other type, InvalidTokenStructure::arrayExpected('headers') is thrown with this message. A JWT header must be a JSON object.","triggerScenarios":"The header segment decodes to a JSON array like '[1,2]' or a bare value like '42' or '\"abc\"' instead of an object; passing an arbitrary base64url string as the first segment.","commonSituations":"Hand-crafted or corrupted tokens; mixing up segment order (passing a payload that is a JSON array as the header); tokens produced by broken custom encoders.","solutions":["Verify the token is issued by a standards-compliant JWT library — header must be a JSON object like {\"typ\":\"JWT\",\"alg\":\"HS256\"}","Catch InvalidTokenStructure and reject the token as malformed","Debug by base64url-decoding the first segment manually and inspecting the JSON","Check your own encoder if you generate tokens — do not json_encode a list as the header"],"exampleFix":"// before\n$token = $parser->parse($jwt); // header decodes to '[\"a\",\"b\"]'\n// after\n$decoded = SodiumBase64Polyfill::base64UrlDecode(explode('.', $jwt)[0]);\nif (!str_starts_with(trim($decoded), '{')) {\n    throw new InvalidArgumentException('JWT header must be a JSON object');\n}\n$token = $parser->parse($jwt);","handlingStrategy":"validation","validationCode":"$h = SodiumBase64Polyfill::base64UrlDecode(explode('.', $jwt)[0]); if (!is_array(json_decode($h, true))) { throw new InvalidArgumentException('Header is not a JSON object'); }","typeGuard":null,"tryCatchPattern":"try { $token = $parser->parse($jwt); } catch (Lcobucci\\JWT\\InvalidTokenStructure $e) { return error_401('Malformed token header'); }","preventionTips":["Issue tokens only with standards-compliant JWT libraries","Inspect decoded segments during debugging to find the corrupt part","Catch InvalidTokenStructure at the auth boundary"],"tags":["jwt","parser","header","php"],"backgroundTag":"unexpected-response-shape","analyzedSha":"375813049c24c7111bda8b6884c57b071ceb2fe7","analyzedAt":"2026-09-14T11:12:28.004Z","contentChangedAt":"2026-09-14T11:12:28.004Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}