jwtk/jjwt · error · java.lang.IllegalStateException

Both 'zip()' and 'compressionCodecResolver' cannot be config

Error message

Both 'zip()' and 'compressionCodecResolver' cannot be configured. Choose either.

What it means

JwtParser.build() forbids configuring both the zip(...) algorithm set and a custom compressionCodecResolver, since they are two competing ways to decide how compressed JWT payloads are decompressed. The builder throws IllegalStateException to resolve the ambiguity up front rather than during parsing.

Source

Thrown at impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParserBuilder.java:403

                throw new IllegalStateException(msg);
            }
        }

        Locator<? extends Key> keyLocator = this.keyLocator; // user configured default, don't overwrite to ensure further build() calls work as expected
        if (keyLocator == null) {
            keyLocator = new ConstantKeyLocator(this.signatureVerificationKey, this.decryptionKey);
        }

        if (!unsecured && unsecuredDecompression) {
            String msg = "'unsecuredDecompression' is only relevant if 'unsecured' is also " +
                    "configured. Please read the JavaDoc of both features before enabling either " +
                    "due to their security implications.";
            throw new IllegalStateException(msg);
        }
        if (this.compressionCodecResolver != null && !Jwts.ZIP.get().equals(this.zipAlgs)) {
            String msg = "Both 'zip()' and 'compressionCodecResolver' " +
                    "cannot be configured. Choose either.";
            throw new IllegalStateException(msg);
        }

        // Invariants.  If these are ever violated, it's an error in this class implementation:
        Assert.stateNotNull(keyLocator, "Key locator should never be null.");

        final DefaultClaims expClaims = (DefaultClaims) this.expectedClaims.build();

        return new DefaultJwtParser(
                provider,
                signingKeyResolver,
                unsecured,
                unsecuredDecompression,
                keyLocator,
                clock,
                critical,
                allowedClockSkewMillis,
                expClaims,
                decoder,

View on GitHub (pinned to fb71496164)

Solutions

  1. Remove compressionCodecResolver(...) and keep zip(...) for standard algorithm selection
  2. Remove zip(...) and keep compressionCodecResolver(...) for fully custom resolution
  3. Audit the builder construction path so only one compression configuration mechanism is used

Example fix

// before
JwtParser parser = Jwts.parser()
    .zip(Jwts.ZIP.DEF)  // plus leftover:
    .compressionCodecResolver(resolver)
    .build();
// after
JwtParser parser = Jwts.parser()
    .zip(Jwts.ZIP.DEF)
    .build();
Defensive patterns

Strategy: validation

Validate before calling

if (codecResolver != null && zipAlgs != null) {
    throw new IllegalArgumentException("Use either zip() or compressionCodecResolver, not both");
}

Try / catch

try {
    JwtParser p = builder.build();
} catch (IllegalStateException e) {
    log.error("Compression config conflict: {}", e.getMessage());
}

Prevention

When it happens

Trigger: Calling JwtParserBuilder.zip(algorithms) and compressionCodecResolver(resolver) on the same builder, then build().

Common situations: Adding a custom codec resolver for a non-standard compression algorithm while a zip(...) whitelist from earlier code remains; wrapping legacy builder code where the newer zip() API was layered over an old resolver.

Related errors


AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09). Data as JSON: /api/errors/db8f49655c338b6f. Report an issue: GitHub.