Description
Affects: Spring Framework 6.1.3 with Tomcat >= 10.1.16 (or Spring Boot >= 3.1.6, including 3.2.x)
Description
When an IOException
is thrown during resolving method argument values for a Rest Controller (in Tomcat's InputBuffer#realReadBytes#313, invoked by Spring's AbstractMessageConverterMethodArgumentResolver#readWithMessageConverters#174), Spring's DefaultHandlerExceptionResolver#handleHttpMessageNotReadable:587 throws an IllegalStateException
with message Cannot call sendError() after the response has been committed
.
This also results in setting HTTP response status code to 500 instead of 400.
In Spring Framework 6.1.3 with Tomcat 10.1.15 the IllegalStateException
is not thrown, and the HTTP status code is 400.
Reproduction
See https://github.com/mgocd/spring-httpmessagenotreadableexception-resolve-issue
Analysis
Starting from Tomcat 10.1.16 (because of this commit) the exception handler inside InputBuffer#realReadBytes
runs response.sendError(400)
, making the httpServletResponse.isCommitted()
return true
starting from this point.
When Spring's DefaultHandlerExceptionResolver#handleHttpMessageNotReadable:587 runs response.sendError(400)
again, it throws the IllegalStateException: Cannot call sendError() after the response has been committed
.
To resolve the issue, DefaultHandlerExceptionResolver#handleHttpMessageNotReadable
could run response.sendError
conditionally, only if !response.isCommitted()
, similarly as done in DefaultHandlerExceptionResolver#handleErrorResponse:514.