spring-projects/spring-security · error · JwtEncodingException

Failed to encode the JWT due to signing error: Failed to…

Error message

Failed to encode the JWT due to signing error: Failed to select a JWK signing key -> + ex.getMessage()

What it means

NimbusJwtEncoder.selectJwk queries the configured JWKSource with a JWKSelector built from the JWT headers; if the source itself throws any Exception while selecting keys, the encoder wraps it in JwtEncodingException with the message "Failed to select a JWK signing key -> <cause>". JWT encoding is aborted because no signing key could be obtained.

Solutions

  1. Read the appended cause message ("-> ...") in the JwtEncodingException to identify the root failure.
  2. Verify the JWKSource's jwks endpoint: it must be reachable and return a valid application/json JWK Set.
  3. Check that the encoder's JWKSource/decoder wiring matches the authorization server's actual JWKS (correct URI, key IDs, algorithms).
  4. If using a custom JWKSource, run it in isolation or add try/catch logging inside get() to surface the internal bug.
  5. Retry encoding after fixing connectivity; transient network errors to the JWKS endpoint are the most common cause.

Example fix

// before
JWKSource<SecurityContext> jwkSource = new RemoteJWKSet<>(new URL("http://wrong-host/jwks.json"));
// after
JWKSource<SecurityContext> jwkSource = new RemoteJWKSet<>(
	new URL("https://auth.example.org/oauth2/jwks")); // reachable, valid JWKS
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight: confirm the JWKSource can produce a key for the intended algorithm
JWKSelector sel = new JWKSelector(new JWKMatcher.Builder().algorithm("RS256").publicOnly(true).build());
if (jwkSource.get(sel, null).isEmpty()) {
	throw new IllegalStateException("JWKSource returned no RS256 signing key; check jwks endpoint/config");
}

Try / catch

try {
	Jwt jwt = encoder.encode(params);
} catch (org.springframework.security.oauth2.jwt.JwtEncodingException ex) {
	if (ex.getMessage().contains("Failed to select a JWK signing key ->")) {
		Throwable cause = ex.getCause();
		log.error("JWK selection failed with root cause", cause);
	}
	throw new IllegalStateException("JWT encoding aborted: JWK source error", ex);
}

Prevention

When it happens

Trigger: encode() with a jwkSource whose get(JWKSelector, context) throws — e.g. a remote JWKSource failing its HTTP fetch, a KeySourceException, a ClassCastException from a misconfigured source, or an exception inside a custom JWKSource implementation.

Common situations: RemoteJWKSet/URL-backed source pointed at an unreachable or wrong jwks-uri; authorization server returning 5xx during key lookup; custom JWKSource with a bug (NPE, unchecked cast); the nested cause message typically reveals a network or configuration problem.

Related errors


AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10). Data as JSON: /api/errors/ab6456ccd4d04b20. Report an issue: GitHub.

Appendix: source

Thrown at oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtEncoder.java:197

		JwtClaimsSet claims = parameters.getClaims();

		JWK jwk = selectJwk(headers);
		headers = addKeyIdentifierHeadersIfNecessary(headers, jwk);

		String jws = serialize(headers, claims, jwk);

		return new Jwt(jws, claims.getIssuedAt(), claims.getExpiresAt(), headers.getHeaders(), claims.getClaims());
	}

	private JWK selectJwk(JwsHeader headers) {
		List<JWK> jwks;
		try {
			JWKSelector jwkSelector = new JWKSelector(createJwkMatcher(headers));
			jwks = this.jwkSource.get(jwkSelector, null);
		}
		catch (Exception ex) {
			throw new JwtEncodingException(String.format(ENCODING_ERROR_MESSAGE_TEMPLATE,
					"Failed to select a JWK signing key -> " + ex.getMessage()), ex);
		}
		if (jwks.isEmpty()) {
			throw new JwtEncodingException(
					String.format(ENCODING_ERROR_MESSAGE_TEMPLATE, "Failed to select a JWK signing key"));
		}
		if (jwks.size() == 1) {
			return jwks.get(0);
		}
		return this.jwkSelector.convert(jwks);
	}

	private String serialize(JwsHeader headers, JwtClaimsSet claims, JWK jwk) {
		JWSHeader jwsHeader = convert(headers);
		JWTClaimsSet jwtClaimsSet = convert(claims);

		JWSSigner jwsSigner = this.jwsSigners.computeIfAbsent(jwk, NimbusJwtEncoder::createSigner);

View on GitHub (pinned to 96852e8860)