spring-projects/spring-security · error · HttpMessageNotReadableException
An error occurred reading the Token Introspection Response:
Error message
An error occurred reading the Token Introspection Response: + ex.getMessage()
What it means
OAuth2TokenIntrospectionHttpMessageConverter.readInternal wraps any exception raised while reading a token introspection response body as JSON and converting it to an OAuth2TokenIntrospection. The body is parsed into a Map<String,Object> by the JSON converter and then mapped by tokenIntrospectionConverter.convert(); any parse or mapping failure is rethrown as org.springframework.http.converter.HttpMessageNotReadableException with the cause attached. This means the introspection response could not be read into the typed model.
Source
Thrown at oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2TokenIntrospectionHttpMessageConverter.java:92
this.jsonMessageConverter = converter;
}
@Override
protected boolean supports(Class<?> clazz) {
return OAuth2TokenIntrospection.class.isAssignableFrom(clazz);
}
@Override
@SuppressWarnings("unchecked")
protected OAuth2TokenIntrospection readInternal(Class<? extends OAuth2TokenIntrospection> clazz,
HttpInputMessage inputMessage) throws HttpMessageNotReadableException {
try {
Map<String, Object> tokenIntrospectionParameters = (Map<String, Object>) this.jsonMessageConverter
.read(STRING_OBJECT_MAP.getType(), null, inputMessage);
return this.tokenIntrospectionConverter.convert(tokenIntrospectionParameters);
}
catch (Exception ex) {
throw new HttpMessageNotReadableException(
"An error occurred reading the Token Introspection Response: " + ex.getMessage(), ex, inputMessage);
}
}
@Override
protected void writeInternal(OAuth2TokenIntrospection tokenIntrospection, HttpOutputMessage outputMessage)
throws HttpMessageNotWritableException {
try {
Map<String, Object> tokenIntrospectionResponseParameters = this.tokenIntrospectionParametersConverter
.convert(tokenIntrospection);
this.jsonMessageConverter.write(tokenIntrospectionResponseParameters, STRING_OBJECT_MAP.getType(),
MediaType.APPLICATION_JSON, outputMessage);
}
catch (Exception ex) {
throw new HttpMessageNotWritableException(
"An error occurred writing the Token Introspection Response: " + ex.getMessage(), ex);
}
}View on GitHub (pinned to 96852e8860)
Solutions
- Check the exception cause to distinguish JSON syntax failure from field-mapping failure; log the raw response body.
- Check the HTTP status before parsing — only feed 2xx application/json responses to the converter.
- Ensure the introspection endpoint returns RFC 7662-compliant JSON (boolean active, correct claim types).
- Verify a JSON-capable converter is set via setJsonMessageConverter and Content-Type is application/json.
- Correct or relax a custom tokenIntrospectionConverter if it rejects otherwise-valid claims.
Example fix
// before
ClientHttpResponse resp = execute(introspectionRequest);
OAuth2TokenIntrospection i = converter.read(OAuth2TokenIntrospection.class, resp); // throws on 401 HTML body
// after
ClientHttpResponse resp = execute(introspectionRequest);
if (resp.getStatusCode() != HttpStatus.OK) {
throw new OAuth2IntrospectionException("Introspection failed: " + resp.getStatusCode());
}
OAuth2TokenIntrospection i = converter.read(OAuth2TokenIntrospection.class, resp); Defensive patterns
Strategy: validation
Validate before calling
// before reading
if (response.getStatusCode() != HttpStatus.OK) throw new OAuth2IntrospectionException("bad status " + response.getStatusCode());
if (!response.getHeaders().getContentType().isCompatibleWith(MediaType.APPLICATION_JSON)) throw new OAuth2IntrospectionException("non-JSON body"); Try / catch
try {
return converter.read(OAuth2TokenIntrospection.class, response);
} catch (HttpMessageNotReadableException e) {
logger.warn("Unreadable introspection response: {}", e.getCause().toString());
return OAuth2TokenIntrospection.builder().active(false).build(); // fail closed
} Prevention
- Fail closed (treat token as inactive) when the introspection response cannot be parsed.
- Reject non-2xx and non-JSON responses before parsing.
- Confirm the introspection endpoint is RFC 7662 compliant (boolean active, correct claim types).
- Watch for proxies/gateways rewriting Content-Type or replacing bodies with HTML error pages.
When it happens
Trigger: Calling OAuth2TokenIntrospectionHttpMessageConverter.read() (via readInternal) when the response body is not valid JSON (HTML error page, empty body, wrong Content-Type), the JSON converter throws, or tokenIntrospectionConverter.convert() rejects the parameters (e.g. active flag not boolean, invalid claim types).
Common situations: The introspection endpoint returns 401/404 with an HTML or empty body that still gets passed to the converter; a proxy/gateway rewrites Content-Type so the Jackson converter refuses the read; an older or non-conformant authorization server omits required claims or sends them with unexpected types; custom converters configured via setTokenIntrospectionConverter are too strict.
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
Related errors
- An error occurred reading the OAuth 2.0 Client Registration:
- An error occurred writing the Token Introspection Response:
- Unable to create an {OAuth2AuthorizedClientManager} bean. Ex
- invalid_dpop_proof
- invalid_scope
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/c687c4d220ae9e75.
Report an issue: GitHub.