Overview
I have an application that allows users to upload a JSON file and parses it using Gson. If the file is invalid, Gson may throw a java.io.EOFException.
Starting with Spring Framework 6.2, due to 9252e741e17d2a810bf084235016692779518ecc, Spring assumes this means the client disconnected and swallows the exception, returning a 200 response and breaking client-side error handling.
To work around this, I would need to handle EOFException in every place that can throw it, replacing it with a different exception.
Related Issues
-
33763
Comment From: rstoyanchev
Could you clarify if this is through GsonHttpMessageConverter in which case I would expect the exception to be wrapped in HttpMessageNotReadableException and that should then be handled in DefaultHandlerExceptionResolver before the disconnected client error check? So I don't quite understand what actually happens.
Comment From: sddavis
It isn't using GsonHttpMessageConverter. It just calls gson.fromJson.
Comment From: rstoyanchev
Okay that clarifies what happens, but more questions on the scenario.
Is it straight forward decoding and if so why not rely on @RequestBody and GsonHttpMessageConverter, which wraps IOException's marking them as originating from message conversion. Or if it is anything more complex, could it not be centralized in order to wrap IOException's more easily? Possibly a custom HttpMessageConverter, if needed, or a common class to delegate to for Gson conversion vs expecting controller methods to use Gson directly.
Comment From: sddavis
A very large JSON file is uploaded and stored. At a later point, in response to a different REST request, the file is parsed using Gson. But more generally, there are lots of reasons that code could throw EOFException, so it's not correct to assume it indicates a client disconnection. Maybe the framework could tell, based on the signature of a controller method, whether it is even possible (or at least likely) that an EOFException thrown by that method is due to a client disconnect?
Comment From: rstoyanchev
So if the EOFException is from the upload, it is a 400 error, but if it is from the endpoint that parses the stored JSON file, it is a 500 error? How do you achieve that without having the exception handled closer to its origin, e.g. an @ExceptionHandler in the controller, or in the controller method itself?
I speak generally as I'm still lacking a good description of your current arrangement. EOFException is a low level exception and a global exception handler can't reason about it other than assuming it's a 500 error, contrary to your case.
We choose to treat it as a disconnected client error as there is not easy alternative, and we can assume that such a broad exception would otherwise be handled closer to its origin where it's easier to interpret.
Comment From: sddavis
Even if the exception is caught by application code, it would be wrapped in another exception with a descriptive error message and rethrown, but the error message will never reach the user because Spring will still see it in the causal chain and assume client disconnected. If we just throw a new exception without the original cause, then it won't be properly logged. So really, to avoid this, every application would need to catch exceptions in every controller method and check for EOFException (because you never know what library might throw that exception, and if it does, it will be very hard to figure out why the client never got a response).
Comment From: rstoyanchev
Even if the exception is caught by application code, it would be wrapped in another exception with a descriptive error message and rethrown, but the error message will never reach the user because Spring will still see it in the causal chain and assume client disconnected
This is not true, is it? The handling is in DefaultHandlerExceptionResolver, which is last in the order of handling. As long as you have a dedicated exception handler, it should be given a chance to handle the exception first.