{"record":{"id":"dc6da866031d9f6a","repo":"jwtk/jjwt","slug":"jws-header-does-not-contain-a-required-alg-algo","errorCode":null,"errorMessage":"JWS header does not contain a required 'alg' (Algorithm) header parameter.  This header parameter is mandatory per the JWS Specification, Section 4.1.1. See https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.1 for more information.","messagePattern":"JWS header does not contain a required 'alg' \\(Algorithm\\) header parameter\\.  This header parameter is mandatory per the JWS Specification, Section 4\\.1\\.1\\. See https://www\\.rfc-editor\\.org/rfc/rfc7515\\.html#section-4\\.1\\.1 for more information\\.","errorType":"exception","errorClass":"MalformedJwtException","httpStatus":null,"severity":"error","filePath":"impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java","lineNumber":401,"sourceCode":"        Map<String, ?> m = deserialize(Streams.of(headerBytes), \"protected header\");\n        Header header;\n        try {\n            header = tokenized.createHeader(m);\n        } catch (Exception e) {\n            String msg = \"Invalid protected header: \" + e.getMessage();\n            throw new MalformedJwtException(msg, e);\n        }\n\n        // https://tools.ietf.org/html/rfc7515#section-10.7 , second-to-last bullet point, note the use of 'always':\n        //\n        //   *  Require that the \"alg\" Header Parameter be carried in the JWS\n        //      Protected Header.  (This is always the case when using the JWS\n        //      Compact Serialization and is the approach taken by CMS [RFC6211].)\n        //\n        final String alg = Strings.clean(header.getAlgorithm());\n        if (!Strings.hasText(alg)) {\n            String msg = tokenized instanceof TokenizedJwe ? MISSING_JWE_ALG_MSG : MISSING_JWS_ALG_MSG;\n            throw new MalformedJwtException(msg);\n        }\n        final boolean unsecured = Jwts.SIG.NONE.getId().equalsIgnoreCase(alg);\n\n        final CharSequence base64UrlDigest = tokenized.getDigest();\n        final boolean hasDigest = Strings.hasText(base64UrlDigest);\n        if (unsecured) {\n            if (tokenized instanceof TokenizedJwe) {\n                throw new MalformedJwtException(JWE_NONE_MSG);\n            }\n            // Unsecured JWTs are disabled by default per the RFC:\n            if (!this.unsecured) {\n                String msg = UNSECURED_DISABLED_MSG_PREFIX + header;\n                throw new UnsupportedJwtException(msg);\n            }\n            if (hasDigest) {\n                throw new MalformedJwtException(JWS_NONE_SIG_MISMATCH_MSG);\n            }\n            if (header.containsKey(DefaultProtectedHeader.CRIT.getId())) {","sourceCodeStart":383,"sourceCodeEnd":419,"githubUrl":"https://github.com/jwtk/jjwt/blob/fb71496164c71442d08adec4571d9616ed5e1b8d/impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java#L383-L419","documentation":"jjwt rejects a compact JWT whose protected header is missing the mandatory 'alg' (Algorithm) header parameter required by RFC 7515 Section 4.1.1. During parse, the header's algorithm value is cleaned and checked; if empty, the token is malformed and cannot be processed, so a MalformedJwtException is thrown (with a JWE-specific message if the token is a JWE).","triggerScenarios":"Calling JwtParser.parse(), parseSignedContent(), or parseSignedClaims() on a compact token whose protected header either omits 'alg' entirely or has an empty/whitespace-only 'alg' value.","commonSituations":"Hand-constructed or manually base64url-encoded tokens; tokens produced by a custom or buggy serializer that drops the alg field; truncated/corrupted tokens where header JSON was altered; third-party token issuers that emit non-compliant headers.","solutions":["Regenerate the token with a compliant JWT library so the header includes a valid 'alg' value (e.g. via JwtBuilder.header().algorithm(...) or default builder behavior).","Inspect the token's first base64url segment (decode it) to confirm whether 'alg' is present and non-empty before parsing.","Fix the upstream issuer/serializer to always emit 'alg'; do not work around it by pre-editing tokens.","If parsing third-party tokens, validate/verify the issuer's tokens are RFC 7515 compliant."],"exampleFix":"// before (hand-built header without alg)\nString token = base64Url(\"{\"typ\":\"JWT\"}\") + \".\" + payload + \".\" + sig;\nJws<Claims> jws = Jwts.parser().verifyWith(key).build().parseSignedClaims(token);\n\n// after (build with jjwt so alg is set automatically)\nString token = Jwts.builder().subject(\"me\")\n    .signWith(key, Jwts.SIG.HS256).compact();","handlingStrategy":"validation","validationCode":"String[] parts = token.split(\"\\\\.\");\nString headerJson = new String(java.util.Base64.getUrlDecoder().decode(parts[0]), java.nio.charset.StandardCharsets.UTF_8);\nif (!headerJson.contains(\"alg\") || io.jsonwebtoken.lang.Strings.hasText(headerJson.replace(\"alg\", \"\").trim()) == false) throw new IllegalArgumentException(\"token header missing alg\");","typeGuard":null,"tryCatchPattern":"try { return parser.parseSignedClaims(token); }\ncatch (io.jsonwebtoken.MalformedJwtException e) { log.warn(\"Token missing alg header\", e); throw new InvalidTokenException(e); }","preventionTips":["Always build tokens with JwtBuilder so alg is emitted automatically","Never hand-assemble compact JWT strings","Validate issuer tokens once in an integration test","Decode and inspect the header segment when debugging parse failures"],"tags":["jwt","jws","malformed-token","missing-header"],"backgroundTag":"schema-validation-failed","analyzedSha":"fb71496164c71442d08adec4571d9616ed5e1b8d","analyzedAt":"2026-09-09T00:33:09.982Z","contentChangedAt":"2026-09-09T00:33:09.982Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}