-
Notifications
You must be signed in to change notification settings - Fork 38.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Exception swallowed in ResponseEntityExceptionHandler [SPR-16743] #21284
Comments
Juergen Hoeller commented From my perspective, As for cause introspection there, this is a pretty old decision that we can't easily change. The differentiating exception can be wrapped in some cases, not least of it all in the common Rossen Stoyanchev, does the above sound feasible to you? |
Jan Mares commented
What concerns the causes and backwards compatibility, I was afraid, that might be a problem. And we (users of the framework) are very glad, that you keep BC breaks to minimum. An ability to choose which exceptions are going to be unwrapped might have sorted out the issue, you are describing, but I guess it is too late for that discussion. Anyways, thank you for looking at this and for working on an amazing framework! |
Rossen Stoyanchev commented Juergen Hoeller, since ResponseEntityExceptionHandler is an As for not checking the the cause, yes that is accidental. |
Juergen Hoeller commented Rossen Stoyanchev, as far as I can tell, it would simply back out from advice-level resolution in |
Jan Mares commented @Rossen Stoyanchev I would just like to make clear, that returning 400 in this scenario is certainly not desirable for us either. The exception cause was If this is not possible due to BC limitations, it would be at least nice to be able to override the behaviour of ResponseEntityExceptionHandler#handleException and force it to stop handling exceptions, where the root should not be handled - e.g. by removing final from it. |
Juergen Hoeller commented As for only handling the root exception level in those cases, this can easily be kept up. Arguably such an advice only means to resolve root-level exceptions, even if it will technically have the chance to introspect wrapped exceptions as well. Revising this towards lenient handling of an unexpected exception instance should do the job here while preserving existing expectations. |
Jan Mares commented
|
Juergen Hoeller commented I actually mean to keep up our check for top-level exceptions in |
Jan Mares commented Alright, sorry for not understanding. |
Rossen Stoyanchev commented
As far as I can see, ExceptionHandlerExceptionResolver finds a matching In other words from the point of view of ExceptionHandlerExceptionResolver, the exception is handled, since it found and successfully invoked the |
Juergen Hoeller commented Rossen Stoyanchev, you're right indeed, that's a quite different code path for a So we might be better off keeping the existing |
Juergen Hoeller commented It turns out that rethrowing the original exception from |
Juergen Hoeller commented Related to this, I've added explicit coverage of root vs cause exception matching to our MVC reference documentation, with hints on how to design custom |
Juergen Hoeller commented Oh, and the actual change is available in the latest |
Jan Mares commented Thank you! |
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
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:
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:
which is not very nice.
Affects: 4.3.16, 5.0.5
Issue Links:
@ExceptionHandler
should match cause as well (e.g. for exception thrown from argument formatter)Referenced from: commits a200df6, 318d04c, 7b894fe, 193c289, ed44262, f2cc70e
Backported to: 4.3.17
The text was updated successfully, but these errors were encountered: