spring-projects/spring-security · error · JwtEncodingException
Failed to select a key since there are multiple for the…
Error message
Failed to select a key since there are multiple for the signing algorithm [%s]; please specify a selector in NimbusJwsEncoder#setJwkSelector
What it means
NimbusJwtEncoder's default jwkSelector cannot decide which key to use when the JWKSource returns more than one JWK matching the signing algorithm/headers. Instead of guessing, it throws JwtEncodingException telling the developer to supply an explicit selector via setJwkSelector. This is a deliberate guard against silently signing with the wrong key.
Solutions
- Call encoder.setJwkSelector(jwks -> jwks.get(0)) if any matching key is acceptable, or better, a selector that filters by kid/key use.
- Prefer narrowing the JWKSource itself (e.g. JWKSet with a single active signing key) so only one key matches.
- Add a distinct "kid" per key and implement a selector that picks the JWK whose.getKeyID() equals the intended kid.
- If keys differ by use, set "use":"sig" on signing keys so non-signing keys no longer match the JWKSelector matcher.
- For rotation, keep exactly one active key in the source used for signing while exposing old keys only for verification.
Example fix
// before
NimbusJwtEncoder encoder = new NimbusJwtEncoder(jwkSource); // multiple matching keys -> throws
// after
encoder.setJwkSelector((jwks) -> jwks.stream()
.filter(jwk -> "my-key-id".equals(jwk.getKeyID()))
.findFirst()
.orElseThrow(() -> new JwtEncodingException("no matching kid"))); Defensive patterns
Strategy: validation
Validate before calling
// Ensure exactly one key will match before encoding, or install a deterministic selector
List<JWK> candidates = jwkSource.get(new JWKSelector(
new JWKMatcher.Builder().algorithm(JWSAlgorithm.RS256.getName()).build()), null);
if (candidates == null || candidates.size() > 1) {
encoder.setJwkSelector(jwks -> jwks.stream()
.filter(j -> "my-kid".equals(j.getKeyID()))
.findFirst()
.orElseThrow());
} Try / catch
try {
Jwt jwt = encoder.encode(params);
} catch (org.springframework.security.oauth2.jwt.JwtEncodingException ex) {
if (ex.getMessage().contains("multiple")) {
// fall back to a kid-based selector configured at startup
}
throw ex;
} Prevention
- Keep only one active signing key per algorithm in the encoder's JWKSource
- Set keyID on every JWK and select by kid in a custom setJwkSelector
- Mark keys with KeyUse.SIGNATURE so non-signing keys don't match
- During key rotation, add new keys for verification but encode with a pinned kid
When it happens
Trigger: Calling NimbusJwtEncoder.encode(JwtEncoderParameters) (or via JwtGenerator) when the configured jwkSource (e.g. an ImmutableJWKSet holding a JWKSet with several keys of the same algorithm/type, or a rotating key set) matches multiple keys for the request's JWS headers, and setJwkSelector was never called with a custom Converter<List<JWK>, JWK>.
Common situations: Key rotation deployments where the JWKS contains old + new keys for the same algorithm; a JWKSet containing both RSA and EC (or multiple RSA) keys that all match the matcher; copying multi-key examples without narrowing by kid; upgrading Spring Security to a version that defaults to throwing instead of picking the first key.
Related errors
- Failed to encode the JWT due to signing error: Failed to…
- Failed to encode the JWT due to signing error: Failed to…
- Failed to encode the JWT due to signing error: Failed to…
- Failed to encode the JWT due to signing error: Failed to…
- Failed to encode the JWT due to signing error: Unable to…
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/86d07ce6d05b05b2.
Report an issue: GitHub.
Appendix: source
Thrown at oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtEncoder.java:115
* @see <a target="_blank" href="https://connect2id.com/products/nimbus-jose-jwt">Nimbus
* JOSE + JWT SDK</a>
*/
public final class NimbusJwtEncoder implements JwtEncoder {
private static final String ENCODING_ERROR_MESSAGE_TEMPLATE = "An error occurred while attempting to encode the Jwt: %s";
private static final JwsHeader DEFAULT_JWS_HEADER = JwsHeader.with(SignatureAlgorithm.RS256).build();
private static final JWSSignerFactory JWS_SIGNER_FACTORY = new DefaultJWSSignerFactory();
private final JwsHeader defaultJwsHeader;
private final Map<JWK, JWSSigner> jwsSigners = new ConcurrentHashMap<>();
private final JWKSource<SecurityContext> jwkSource;
private Converter<List<JWK>, JWK> jwkSelector = (jwks) -> {
throw new JwtEncodingException(
String.format(
"Failed to select a key since there are multiple for the signing algorithm [%s]; "
+ "please specify a selector in NimbusJwsEncoder#setJwkSelector",
jwks.get(0).getAlgorithm()));
};
/**
* Constructs a {@code NimbusJwtEncoder} using the provided parameters.
* @param jwkSource the {@code com.nimbusds.jose.jwk.source.JWKSource}
*/
public NimbusJwtEncoder(JWKSource<SecurityContext> jwkSource) {
this.defaultJwsHeader = DEFAULT_JWS_HEADER;
Assert.notNull(jwkSource, "jwkSource cannot be null");
this.jwkSource = jwkSource;
}
private NimbusJwtEncoder(JWK jwk) {
Assert.notNull(jwk, "jwk cannot be null");View on GitHub (pinned to 96852e8860)