spring-projects/spring-security · error · BadJwtException
An error occurred while attempting to decode the Jwt
Error message
An error occurred while attempting to decode the Jwt: %s
What it means
parse() also handles non-ParseException failures from JWTParser.parse (e.g. IllegalStateException, IllegalCallerException, IO errors on nested objects); it wraps the underlying exception message into BadJwtException via the DECODING_ERROR_MESSAGE_TEMPLATE. The %s is filled with ex.getMessage().
Solutions
- Read the wrapped message (and cause) to identify the specific parse failure; log the exception chain server-side.
- Regenerate the token from the authorization server and compare headers/claims with the failing token.
- Ensure token claims use standard JSON types; avoid exotic claim serialization on the issuer side.
- Catch BadJwtException and reject the request with 401 invalid_token.
Example fix
// before
catch (BadJwtException e) { /* message ignored */ }
// after
catch (BadJwtException e) {
logger.warn("JWT rejected: {} cause={}", e.getMessage(), e.getCause(), e);
// return 401 with WWW-Authenticate: Bearer error="invalid_token"
} Defensive patterns
Strategy: try-catch
Try / catch
try { return jwtDecoder.decode(token); }
catch (BadJwtException e) {
log.warn("JWT parse failure: {}", e.getMessage(), e.getCause());
// respond 401 invalid_token
} Prevention
- Log the full exception chain (BadJwtException + cause) to identify the underlying Nimbus failure
- Keep Nimbus and Spring Security versions aligned to avoid parse-behavior surprises
- Issue tokens with standard claim types only; avoid non-standard nested serialization
When it happens
Trigger: decode() → parse(token) where parsing throws a RuntimeException other than ParseException — e.g. nested/serialized JSON claims that fail to deserialize, illegal header parameter combinations detected by Nimbus.
Common situations: Tokens with non-standard nested claim encodings; corrupted JWTs whose structure parses superficially but fails deeper validation; custom Nimbus configuration or version differences changing parse behavior.
Related errors
- An error occurred while attempting to decode the Jwt…
- An error occurred reading the OAuth 2.0 Access Token…
- An error occurred reading the OpenID Client Registration
- An error occurred reading the OpenID Provider Configuration
- An error occurred while attempting to decode the Jwt…
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/51c084b61f424653.
Report an issue: GitHub.
Appendix: source
Thrown at oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoder.java:158
JWT jwt = parse(token);
if (jwt instanceof PlainJWT) {
this.logger.trace("Failed to decode unsigned token");
throw new BadJwtException("Unsupported algorithm of " + jwt.getHeader().getAlgorithm());
}
Jwt createdJwt = createJwt(token, jwt);
return validateJwt(createdJwt);
}
private JWT parse(String token) {
try {
return JWTParser.parse(token);
}
catch (Exception ex) {
this.logger.trace("Failed to parse token", ex);
if (ex instanceof ParseException) {
throw new BadJwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, "Malformed token"), ex);
}
throw new BadJwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, ex.getMessage()), ex);
}
}
private Jwt createJwt(String token, JWT parsedJwt) {
try {
// Verify the signature
JWTClaimsSet jwtClaimsSet = this.jwtProcessor.process(parsedJwt, null);
Map<String, Object> headers = new LinkedHashMap<>(parsedJwt.getHeader().toJSONObject());
Map<String, Object> claims = this.claimSetConverter.convert(jwtClaimsSet.getClaims());
// @formatter:off
return Jwt.withTokenValue(token)
.headers((h) -> h.putAll(headers))
.claims((c) -> c.putAll(claims))
.build();
// @formatter:on
}
catch (RemoteKeySourceException ex) {
this.logger.trace("Failed to retrieve JWK set", ex);View on GitHub (pinned to 96852e8860)