jwtk/jjwt · error · IllegalStateException

Both a 'signingKeyResolver and a 'verifyWith' key cannot be…

Error message

Both a 'signingKeyResolver and a 'verifyWith' key cannot be configured. Choose either, or prefer `keyLocator` when possible.

What it means

Thrown as IllegalStateException from DefaultJwtParserBuilder.build() when both a SigningKeyResolver and an explicit verifyWith key have been configured. These two key sources are mutually exclusive for JWS verification — the builder cannot decide which to use — so building the parser fails fast.

Solutions

  1. Remove the setSigningKeyResolver(...) call if all tokens are verified with one static key.
  2. Remove verifyWith(...) if the resolver is needed to select keys per-token (e.g. per-kid lookup).
  3. Prefer keyLocator (per the message) for dynamic key selection; ensure resolver/verifyWith are unset when using it.
  4. Guard shared configuration code so exactly one of {keyLocator, signingKeyResolver, signatureVerificationKey} is set — assert before build().

Example fix

// before
Jwts.parser()
    .setSigningKeyResolver(resolver)
    .verifyWith(publicKey) // conflicts
    .build();
// after
Jwts.parser().verifyWith(publicKey).build();
// or for per-token key choice:
Jwts.parser().keyLocator(locate -> byKid(locate.getHeader().get("kid", String.class))).build();
Defensive patterns

Strategy: validation

Validate before calling

// before build(): ensure exactly one JWS key source is configured
int sources = (resolver != null ? 1 : 0) + (verifyKey != null ? 1 : 0) + (keyLocator != null ? 1 : 0);
if (sources > 1) throw new IllegalStateException("Configure only one of signingKeyResolver/verifyWith/keyLocator");

Try / catch

try {
    JwtParser parser = Jwts.parser().verifyWith(key).build();
} catch (IllegalStateException e) {
    // conflicting key configuration; strip resolver or verifyWith
}

Prevention

When it happens

Trigger: parserBuilder().setSigningKeyResolver(resolver).verifyWith(key)...build(); typically after adding verifyWith to existing resolver-based code, or when shared builder-configuration code sets both.

Common situations: Gradual migration from the deprecated signingKeyResolver API to verifyWith leaving both set; framework/interceptor code that configures a resolver conditionally while other code sets a static key; copy-pasted parser configuration accumulating settings.

Related errors


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

Appendix: source

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

    @SuppressWarnings("deprecation")
    @Override
    public JwtParserBuilder setCompressionCodecResolver(CompressionCodecResolver resolver) {
        this.compressionCodecResolver = Assert.notNull(resolver, "CompressionCodecResolver cannot be null.");
        return this;
    }

    @Override
    public JwtParser build() {

        if (this.deserializer == null) {
            //noinspection unchecked
            json(Services.get(Deserializer.class));
        }
        if (this.signingKeyResolver != null && this.signatureVerificationKey != null) {
            String msg = "Both a 'signingKeyResolver and a 'verifyWith' key cannot be configured. " +
                    "Choose either, or prefer `keyLocator` when possible.";
            throw new IllegalStateException(msg);
        }
        if (this.keyLocator != null) {
            if (this.signatureVerificationKey != null) {
                String msg = "Both 'keyLocator' and a 'verifyWith' key cannot be configured. " +
                        "Prefer 'keyLocator' if possible.";
                throw new IllegalStateException(msg);
            }
            if (this.decryptionKey != null) {
                String msg = "Both 'keyLocator' and a 'decryptWith' key cannot be configured. " +
                        "Prefer 'keyLocator' if possible.";
                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);
        }

View on GitHub (pinned to fb71496164)