Skip to content

Exception swallowed in ResponseEntityExceptionHandler [SPR-16743] #21284

@spring-projects-issues

Description

@spring-projects-issues

Jan Mares opened SPR-16743 and commented

Problem - after calling other service with RestTemplate, the call fails because of deserialization failure, but the exception is not logged

Expected behaviour - stack trace of the exception can be found in the logs

Actual behaviour - only this line appears in the log and the server returns 500

c.d.s.p.c.c.e.BadRequestExceptionHandler : Unknown exception type: org.springframework.web.client.RestClientException

Reason - exception is swallowed in ResponseEntityExceptionHandler (https://github.com/spring-projects/spring-framework/blob/v5.0.5.RELEASE/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java#L188) because not only type of the exception, but also the type of its cause is used to match method in ExceptionHandlerMethodResolver(https://github.com/spring-projects/spring-framework/blob/v5.0.5.RELEASE/spring-web/src/main/java/org/springframework/web/method/annotation/ExceptionHandlerMethodResolver.java#L136)

Code:

@ControllerAdvice
public class BadRequestExceptionHandler extends ResponseEntityExceptionHandler
{
 
    @ExceptionHandler(value = {BadRequestException.class})
    protected ResponseEntity<Object> handleBadRequest(BadRequestException ex, WebRequest request)
	{
        return handleExceptionInternal(ex, ex.getMessage(), new HttpHeaders(), HttpStatus.BAD_REQUEST, request);
    }
}

The swallowing most probably happens, because exception org.springframework.web.client.RestClientException is thrown with HttpMessageNotReadableException as its cause. When ExceptionHandlerMethodResolver looks for a handler method in resolveMethodByThrowable, first it does not find one, but when it tries a cause of the exception (HttpMessageNotReadableException) it finds one in ResponseEntityExceptionHandler::handleException. That results in this method being called, but the instance passed to the method is not the one of HttpMessageNotReadableException, but of RestClientException and therefore it does not match any of the instanceof ifs in the method and falls in the last else, resulting in logging only that little fragment shown above.

IMHO trying to match against cause as well as the exception itself is dangerous and rather unexpected. As we reuse code, we need to reuse the exceptions that come with it. But in this case an exception of type HttpMessageNotReadableException thrown from contoller trying to deserialize the request is something completely different to the same exception wrapped in RestClientException when trying to parse a response of dependant microservice. Also, why only one level down, why not two levels - cause of a cause, or three cause of a cause of a cause ...

Hacky temporary solution - as we cannot override handleException in ResponseEntityExceptionHandler , we had to override handleInternalException in the following way:

@Override
protected ResponseEntity<Object> handleExceptionInternal(
     final Exception ex,
     final Object body,
     final HttpHeaders headers,
     final HttpStatus status,
     final WebRequest request
)
{
     if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) {
          // due to problem with cause and the fact the handleException in the parent is final
          logger.warn("Potentially unlogged exception. Re-logging: ", ex);
     }
     return super.handleExceptionInternal(ex, body, headers, status, request);
}

which is not very nice.


Affects: 4.3.16, 5.0.5

Issue Links:

Referenced from: commits a200df6, 318d04c, 7b894fe, 193c289, ed44262, f2cc70e

Backported to: 4.3.17

Metadata

Metadata

Assignees

Labels

in: webIssues in web modules (web, webmvc, webflux, websocket)status: backportedAn issue that has been backported to maintenance branchestype: bugA general bug

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions