You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Use 500 for default exception handling when DisconnectedClientHelper#clientDisconnectedException returns true #34481
I stumbled upon #34264 which caused some bigger issues internally because some server side connection resets resulted in a 200 successful response. While the biggest issue was solved I think that it is quite unsettling that an exception results in a 200 response and this is still the case for the current spring version (6.2.3) shipped with spring boot 3.4.3.
Minimal example which returns a 200 even though an exception is thrown and not properly handled:
@RestController
@ControllerAdvice
public class DemoController {
private static final Logger log = LoggerFactory.getLogger(DemoController.class);
@GetMapping
public String demo() {
throw new RuntimeException("connection reset by peer");
}
@ExceptionHandler(RuntimeException.class)
public ResponseEntity<Object> handleRuntimeException(RuntimeException e) {
log.error(e.getMessage());
throw e;
}
}
While this usecase might not look to useful we definitely have use cases where we want to handle an exception in some custom way using an exception handler but only in specific cases. So something like this:
@ExceptionHandler(RuntimeException.class)
public ResponseEntity<Object> handleRuntimeException(RuntimeException e) {
if (matchesSomeCriteria(e)) {
return specialHandling(e);
}
throw e;
}
While investigating I found that this is probably caused here:
if (disconnectedClientHelper.checkAndLogClientDisconnectedException(invocationEx)) {
returnnewModelAndView();
}
If I understand this correctly the idea is to not continue handling exceptions in case the client closed the connection. Since the check for this is just using some string comparison on the exception message it is quite easy to have an exception matching this as can be seen above.
If no one has ideas on how to fix this properly I would suggest to at least set a 500 error status on the ModelView that is created in the ExceptionHandlerExceptionResolver to make this a little less worrying. While the behaviour might still be unexpected it is at least not returning successful responses for cases where disconnectedClientHelper.checkAndLogClientDisconnectedException returns true even though some internal exception occurred.
If you agree with my findings I can create a PR for this.
For "connection reset by peer" or "broken pipe" I would expect the exception type is an instance of IOException, and we could add that check to make it more specific.
However, it would be useful to find out more about your case. Is it exactly RuntimeException with the phrase "connection reset by peer", is it raised by a JDK library class, or some other 3rd party library, and also what is the connection it is referring to?
I would also like to confirm that #34264 is not a regression. As far as I can tell it didn't change anything for your case.
I think we have a small misunderstanding. The actual problem we had is fixed with #34264 being fixed. I just stumbled upon this unexpected behaviour because of that bug. This issue is the suggestion to implement the following improvement: There should not be a case where an exception that contains some "random" string in the message results in a 200.
The example I provided above is mostly for reproduction. But I currently see the following two possibilities where a 200 response is returned even though an exception was thrown:
The thrown exception contains "connection reset by peer" or "broken pipe". I can definitely imagine cases where this is done without having any idea that this might cause problems in the Spring framework.
There is another bug introduced in the DiconnectedClientHelper which again results in matching exceptions to broadly and by that returning a success response.
So my goal with this issue is to suggest to make this less error prone by making sure that there is no path in the code which can lead to a 200 in case of exceptions.
And if I understand the code in the ExceptionHandlerExceptionResolver correctly the case is about the client closing the connection. In that case the ExceptionHandler chain should not be evaluated anymore since the client is "gone" anyway. And this is achieved by returning an empty response with the status 200. The easiest improvement in my opinion is to make this empty response a 500. In cases where the exception was correctly matched as a client disconnect this does not change anything because there is no client anymore to care about the response. In cases where the exception was incorrectly matched (for whatever reason) we make sure that there is no successful response when actually an error happened.
Reacted by Johan Blumenberg, Johan Blumenberg and Thijs Lemmens
Hello, we ran into this, DefaultHandlerExceptionResolver finds an EoFException inside a DataAccessResourceFailureException thrown when HikariCP can't connect to the DB, and the response is 200 without a body
This IMHO is a very serious breach of contract!
We ran into the same issue as well when connecting to MySQL via tomcat jdbc. It seems the criteria that DisconnectedClientHelper uses to determine whether an exception was due to a remote client disconnecting is too broad.
In our case, we run spring boot as a web server that connects to a database. If there are intermittent connection errors between our application and the database, a java.io.EOFException is thrown. In this instance, I would expect our web server to respond with a 5xx error to the caller. However, this exception ends up being swallowed by spring and a 200 with an empty response body is returned instead
This behaviour is quite unexpected and there is no way to override it.
Could DisconnectedClientHelper (or the classes that use DisconnectedClientHelper) be configurable such that we can opt out of this behaviour?
@NiFNi I understood what you brought up, but we have different suggestions. I think "connection reset by peer" or "broken pipe" are not random phrases and are very specific, but my suggestion was to strengthen the check of these phrases by also checking the exception type is IOException, which in my opinion makes a strong enough signal. Your suggestion is to set the status to 500, but this is not without potential side effects as the 500 status would trigger further error handling, and likely an ERROR dispatch where the goal of this is to actually minimise such errors.
@manos-orfanoudakis-newcross, @ThousandEyes-Derek, @michaldo, the cases you raise are closely related, but relate to default handling in DefaultHandlerExceptionResolver. I acknowledge that the current logic does not have a good way to differentiate between disconnection of the original client vs some onward remote server. Certainly we can add DataAccessResourceFailureException to the list of wrapper exceptions that would filter out. For MongoSocketWriteException, I wonder does get wrapped in some org.springframework or org.springframework.data exception?
@michaldo I have created a separate issue #36134. In the mean time, you should be able to add exception handler for this, and that should handle such an exception before it gets to DefaultHandlerExceptionResolver.
I stumbled on this bug as well today. In my opinion, having the framework secretly returning a 200 OK response on an exception in any situation would be a total disaster.
A big problem in this case is that it is silent, because logs are reduced to debug or trace level, and that it is really hard to know about this "feature" until you run into it. Most times you probably never notice.
Reading about the DisconnectedClientHelper class shows a history of erroneously categorising the wrong exceptions as the client closing the connection.
All the times in the past when my services have returned a 200 response, and the problems that this might have caused. How do I find all the cases in the past when my customers think that their payment went through, but in fact it failed?
To give this some real life edge:
When I found this problem we did exactly that. Because we returned a 200 in a payment API the client thought the payment went through and handed out the product even though there was no successful payment...
I totally agree with @johanblumenberg other comments as well. It is not about known exception because you can handle those. But no one hinders developers not knowing about this to include the message that is matched for in new exceptions. Unless you were a victim of this issue you cannot know that this might cause rather big problems.
Suggestion: Make DisconnectedClientHelper extensible or configurable
We hit this issue in production — a java.io.EOFException: "DEF length 553 object truncated by 1" (an error on a malformed signature validation) is being classified as a client disconnect by DisconnectedClientHelper. As a result, spring silently swallows the exception, and returns an HTTP 200 with an empty body instead of the expected 400 or at least 500 error response.
This was extremely difficult to diagnose because:
No @ExceptionHandler is invoked
No /error dispatch occurs
DefaultErrorController / DefaultErrorAttributes are never called
The only visible symptom is a silent 200 with empty body
The root cause is that DisconnectedClientHelper.isClientDisconnectedException() is a static method with hardcoded phrase matching. There is currently no way to:
Override the disconnect detection logic (no interface/strategy pattern)
Disable it via a configuration property
Subclass DisconnectedClientHelper and substitute it
Thank you for the great framework — this is a small but impactful gap in extensibility. 🙏
@johanblumenberg have you tried with Spring Framework 6.2.16? It includes a backport of #36134. If you did, could you clarify what the exception type is that wraps the IOException cause?
We are running into the same issue when we get an error while writing to S3 using the Minio SDK. However, the specific cases we mention here are not really the point. There will always be a lot of cases that will wrongfully match.
We can revise the handling of such exceptions along the lines of what @NiFNi suggested, by setting the response to 500, e.g. in DefaultHandlerExceptionResolver for Spring MVC. This wouldn't change anything for a client that went away, but it would send the correct status for any other connection issue. The main goal in any case is to avoid unnecessary error logging.
That said setting the response status to 500 could lead to more error handling such as an error dispatch, and have some side effects on the server side like logging, so we will make the change for 7.1.
In the mean time, as a workaround you can do as suggested in #34481 (comment) or have a global exception handler that checks if an exception matches DisconnectedClientHelper.isClientDisconnectedException and handle it.
changed the title [-]ExceptionHandlerExceptionResolver results in 200 for some special exceptions[/-][+]Use 500 for default exception handling when DisconnectedClientHelper#clientDisconnectedException returns true[/+]on Jun 1, 2026
@michaldo we can't check directly for a Tomcat exception. We use DisconnectedClientHelper for that, but AbstractJacksonHttpMessageConverter is not a good place for such checks because it does not know if it is writing to the Servlet response or to another server (via RestClient).
If anything we could revise the order in DefaultHandlerExceptionResolver and by then HttpMessageNotWritableException would have been wrapped as RestClientException if coming from RestClient, and DisconnectedClientHelper filters that out.
That said given that both handleHttpMessageNotWritable and handleDisconnectedClientException attempt to send a 500, I am wondering are you actually experiencing this and what is the outcome that you see?
Actually, when connection to the client is broken, I observe a warning log entry "Ignoring exception, response committed already...". I do not want to be disturbed by such false warning, because it is client problem, not server problem.
Regarding the solution, when I read long story of current issue, I have feeling that existing architecture does not reflect natural logic flow. The flow is:
server do a lot of work
server returns answer to client
When server work in (2) is interrupted by broken-pipe/client-abort-exception then should silently skip. But in existing architecture exception stacktrace deduction must be applied instead
I understand my proposal is wrong because if AbstractJacksonHttpMessageConverter is modified then interruption in (1) and (2) will be indistinguishable.
I'm afraid I don't have constructive proposal how to solve the issue. I have not architecture big picture in mind, by I feel architecture should better mirror server processing flow.
PS. I have one pragmatic but non ideal solution. DefaultHandlerExceptionResolver#handleHttpMessageNotWritable should not log warn when response in committed. What is chance for coincidence that application has own, real problem with HttpMessageConverter and same time client interrupted connection? Minimal
Activity
rstoyanchev commentedon Feb 26, 2025
For "connection reset by peer" or "broken pipe" I would expect the exception type is an instance of
IOException, and we could add that check to make it more specific.However, it would be useful to find out more about your case. Is it exactly
RuntimeExceptionwith the phrase "connection reset by peer", is it raised by a JDK library class, or some other 3rd party library, and also what is the connection it is referring to?I would also like to confirm that #34264 is not a regression. As far as I can tell it didn't change anything for your case.
NiFNi commentedon Feb 28, 2025
@rstoyanchev thanks for the reply!
I think we have a small misunderstanding. The actual problem we had is fixed with #34264 being fixed. I just stumbled upon this unexpected behaviour because of that bug. This issue is the suggestion to implement the following improvement: There should not be a case where an exception that contains some "random" string in the message results in a 200.
The example I provided above is mostly for reproduction. But I currently see the following two possibilities where a 200 response is returned even though an exception was thrown:
So my goal with this issue is to suggest to make this less error prone by making sure that there is no path in the code which can lead to a 200 in case of exceptions.
And if I understand the code in the ExceptionHandlerExceptionResolver correctly the case is about the client closing the connection. In that case the ExceptionHandler chain should not be evaluated anymore since the client is "gone" anyway. And this is achieved by returning an empty response with the status 200. The easiest improvement in my opinion is to make this empty response a 500. In cases where the exception was correctly matched as a client disconnect this does not change anything because there is no client anymore to care about the response. In cases where the exception was incorrectly matched (for whatever reason) we make sure that there is no successful response when actually an error happened.
manos-orfanoudakis-newcross commentedon Jun 3, 2025
Hello, we ran into this,
DefaultHandlerExceptionResolverfinds anEoFExceptioninside aDataAccessResourceFailureExceptionthrown when HikariCP can't connect to the DB, and the response is 200 without a bodyThis IMHO is a very serious breach of contract!
ThousandEyes-Derek commentedon Aug 22, 2025
We ran into the same issue as well when connecting to MySQL via tomcat jdbc. It seems the criteria that DisconnectedClientHelper uses to determine whether an exception was due to a remote client disconnecting is too broad.
In our case, we run spring boot as a web server that connects to a database. If there are intermittent connection errors between our application and the database, a java.io.EOFException is thrown. In this instance, I would expect our web server to respond with a 5xx error to the caller. However, this exception ends up being swallowed by spring and a 200 with an empty response body is returned instead
This behaviour is quite unexpected and there is no way to override it.
Could DisconnectedClientHelper (or the classes that use DisconnectedClientHelper) be configurable such that we can opt out of this behaviour?
michaldo commentedon Dec 23, 2025
Please continue the issue. My case when DisconnectedClientHelper wrongly detects database failure as disconnected client is:
rstoyanchev commentedon Jan 13, 2026
@NiFNi I understood what you brought up, but we have different suggestions. I think "connection reset by peer" or "broken pipe" are not random phrases and are very specific, but my suggestion was to strengthen the check of these phrases by also checking the exception type is
IOException, which in my opinion makes a strong enough signal. Your suggestion is to set the status to 500, but this is not without potential side effects as the 500 status would trigger further error handling, and likely an ERROR dispatch where the goal of this is to actually minimise such errors.@manos-orfanoudakis-newcross, @ThousandEyes-Derek, @michaldo, the cases you raise are closely related, but relate to default handling in
DefaultHandlerExceptionResolver. I acknowledge that the current logic does not have a good way to differentiate between disconnection of the original client vs some onward remote server. Certainly we can addDataAccessResourceFailureExceptionto the list of wrapper exceptions that would filter out. ForMongoSocketWriteException, I wonder does get wrapped in someorg.springframeworkororg.springframework.dataexception?michaldo commentedon Jan 13, 2026
In my case MongoSocketWriteException was wrapped in DataAccessResourceFailureException
rstoyanchev commentedon Jan 13, 2026
@michaldo I have created a separate issue #36134. In the mean time, you should be able to add exception handler for this, and that should handle such an exception before it gets to
DefaultHandlerExceptionResolver.johanblumenberg commentedon Feb 23, 2026
Hi,
I stumbled on this bug as well today. In my opinion, having the framework secretly returning a 200 OK response on an exception in any situation would be a total disaster.
A big problem in this case is that it is silent, because logs are reduced to debug or trace level, and that it is really hard to know about this "feature" until you run into it. Most times you probably never notice.
Reading about the
DisconnectedClientHelperclass shows a history of erroneously categorising the wrong exceptions as the client closing the connection.I completely agree with the comment from @NiFNi above: #34481 (comment)
100% agree with this.
4 remaining items
NiFNi commentedon Mar 7, 2026
To give this some real life edge:
When I found this problem we did exactly that. Because we returned a 200 in a payment API the client thought the payment went through and handed out the product even though there was no successful payment...
I totally agree with @johanblumenberg other comments as well. It is not about known exception because you can handle those. But no one hinders developers not knowing about this to include the message that is matched for in new exceptions. Unless you were a victim of this issue you cannot know that this might cause rather big problems.
karlovskiy commentedon Apr 6, 2026
Suggestion: Make
DisconnectedClientHelperextensible or configurableWe hit this issue in production — a
java.io.EOFException: "DEF length 553 object truncated by 1"(an error on a malformed signature validation) is being classified as a client disconnect byDisconnectedClientHelper. As a result, spring silently swallows the exception, and returns an HTTP 200 with an empty body instead of the expected 400 or at least 500 error response.This was extremely difficult to diagnose because:
@ExceptionHandleris invoked/errordispatch occursDefaultErrorController/DefaultErrorAttributesare never calledThe root cause is that
DisconnectedClientHelper.isClientDisconnectedException()is astaticmethod with hardcoded phrase matching. There is currently no way to:DisconnectedClientHelperand substitute itThank you for the great framework — this is a small but impactful gap in extensibility. 🙏
thijslemmens commentedon May 18, 2026
We are running into the same issue when we get an error while writing to S3 using the Minio SDK. However, the specific cases we mention here are not really the point. There will always be a lot of cases that will wrongfully match.
We had to override the DefaultHandlerExceptionResolver with our own:
https://github.com/xenit-eu/contentgrid-appserver/blob/89458b4b9fc5d2766b90e89bbde9e610f339dab1/contentgrid-appserver-rest/src/main/java/com/contentgrid/appserver/rest/ContentGridRestConfiguration.java#L136
rstoyanchev commentedon Jun 1, 2026
We can revise the handling of such exceptions along the lines of what @NiFNi suggested, by setting the response to 500, e.g. in
DefaultHandlerExceptionResolverfor Spring MVC. This wouldn't change anything for a client that went away, but it would send the correct status for any other connection issue. The main goal in any case is to avoid unnecessary error logging.That said setting the response status to 500 could lead to more error handling such as an error dispatch, and have some side effects on the server side like logging, so we will make the change for 7.1.
In the mean time, as a workaround you can do as suggested in #34481 (comment) or have a global exception handler that checks if an exception matches
DisconnectedClientHelper.isClientDisconnectedExceptionand handle it.[-]ExceptionHandlerExceptionResolver results in 200 for some special exceptions[/-][+]Use 500 for default exception handling when DisconnectedClientHelper#clientDisconnectedException returns true[/+]Revise disconnected client error handling in Spring MVC
michaldo commentedon Aug 10, 2026
I would like to resume this issue because current solution has a drawback. (spring-frawework 7.0.8)
When code attempts to write JSON to closed stream, two actions are performed, 2 actions are performed
Let see big decision tree org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver#doResolveException fragment:
That is pretty clear - one by one possible category is detected, last considered category is "broken-pipe"
The issue is in action 1, executed within
org.springframework.http.converter.AbstractJacksonHttpMessageConverter#writeInternal fragment:
IMHO, code should be:
because problem is not on writing JSON side but on client side. Message itself is writeable, but reader has gone.
Then exception will reach big decision tree and will go to branch
else if (DisconnectedClientHelper.isClientDisconnectedException(ex))rstoyanchev commentedon Aug 18, 2026
@michaldo we can't check directly for a Tomcat exception. We use
DisconnectedClientHelperfor that, butAbstractJacksonHttpMessageConverteris not a good place for such checks because it does not know if it is writing to the Servlet response or to another server (via RestClient).If anything we could revise the order in
DefaultHandlerExceptionResolverand by thenHttpMessageNotWritableExceptionwould have been wrapped asRestClientExceptionif coming fromRestClient, andDisconnectedClientHelperfilters that out.That said given that both
handleHttpMessageNotWritableandhandleDisconnectedClientExceptionattempt to send a 500, I am wondering are you actually experiencing this and what is the outcome that you see?michaldo commentedon Aug 18, 2026
Hi,
thanks for answer.
Actually, when connection to the client is broken, I observe a warning log entry "Ignoring exception, response committed already...". I do not want to be disturbed by such false warning, because it is client problem, not server problem.
Regarding the solution, when I read long story of current issue, I have feeling that existing architecture does not reflect natural logic flow. The flow is:
When server work in (2) is interrupted by broken-pipe/client-abort-exception then should silently skip. But in existing architecture exception stacktrace deduction must be applied instead
I understand my proposal is wrong because if AbstractJacksonHttpMessageConverter is modified then interruption in (1) and (2) will be indistinguishable.
I'm afraid I don't have constructive proposal how to solve the issue. I have not architecture big picture in mind, by I feel architecture should better mirror server processing flow.
PS. I have one pragmatic but non ideal solution. DefaultHandlerExceptionResolver#handleHttpMessageNotWritable should not log warn when response in committed. What is chance for coincidence that application has own, real problem with HttpMessageConverter and same time client interrupted connection? Minimal
rstoyanchev commentedon Aug 18, 2026
Could you create a separate issue for this request? It is related but sufficiently independent, and the current issue is already very long.