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

  1. Check the exception cause to distinguish JSON syntax failure from field-mapping failure; log the raw response body.
  2. Check the HTTP status before parsing — only feed 2xx application/json responses to the converter.
  3. Ensure the introspection endpoint returns RFC 7662-compliant JSON (boolean active, correct claim types).
  4. Verify a JSON-capable converter is set via setJsonMessageConverter and Content-Type is application/json.
  5. 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

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


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