Note: This issue was originally posted as a Spring Boot issue (spring-boot#46036
), but I was informed by a Spring member that the issue was with the Spring Framework and to post the issue here.
Description
Spring version: 6.2.4
Spring Boot version: 3.4.4
Java version: 17
Maven version: 3.9.0
I have a Spring Boot app that:
1. extends the OncePerRequestFilter
to add headers, namely Content-Type
and X-Content-Type-Options
to every response.
2. extends the ResponseEntityExceptionHandler
to enable custom exception handling for the IllegalArgumentException
.
3. uses Jetty instead of Tomcat.
When I upgraded from Spring 6.2.3
(Spring Boot 3.4.3
) to Spring 6.2.4
(Spring Boot 3.4.4
), I observed a null
value for the charset
encoding in the Content-Type
response header when I try to access an endpoint/resource that doesn't exist. For the previous version 6.2.3
, there was no charset
in the first place. null
is not recognized as a legal charset
so a different Spring Boot REST client that calls this app fails with:
Caused by: org.springframework.util.InvalidMimeTypeException: Invalid mime type "application/problem+json;charset=null": unsupported charset 'null'
Reproduction
A minimal application that reproduces this error is attached as a demo.zip
file with this issue: demo.zip
Download it, extract it to your local machine, build the app (mvn -DskipTests clean package
) and run it (java -jar target\demo-0.0.1-SNAPSHOT.jar
)...
With Spring 6.2.4
:
>curl -v localhost:8080/repro * Uses proxy env variable no_proxy == '127.0.0.1,localhost' * Host localhost:8080 was resolved. * IPv6: ::1 * IPv4: 127.0.0.1 * Trying [::1]:8080... * Connected to localhost (::1) port 8080 > GET /repro HTTP/1.1 > Host: localhost:8080 > User-Agent: curl/8.9.1 > Accept: */* > * Request completely sent off < HTTP/1.1 404 Not Found < Date: Thu, 19 Jun 2025 18:51:06 GMT < X-Content-Type-Options: nosniff < Vary: Origin < Vary: Access-Control-Request-Method < Vary: Access-Control-Request-Headers < Content-Type: application/problem+json;charset=null < Transfer-Encoding: chunked < {"type":"about:blank","title":"Not Found","status":404,"detail":"No static resource repro.","instance":"/repro"}* Connection #0 to host localhost left intact
With Spring 6.2.3
:
>curl -v localhost:8080/repro * Uses proxy env variable no_proxy == '127.0.0.1,localhost' * Host localhost:8080 was resolved. * IPv6: ::1 * IPv4: 127.0.0.1 * Trying [::1]:8080... * Connected to localhost (::1) port 8080 > GET /repro HTTP/1.1 > Host: localhost:8080 > User-Agent: curl/8.9.1 > Accept: */* > * Request completely sent off < HTTP/1.1 404 Not Found < Date: Thu, 19 Jun 2025 18:52:40 GMT < X-Content-Type-Options: nosniff < Content-Type: application/json < Vary: Origin < Vary: Access-Control-Request-Method < Vary: Access-Control-Request-Headers < Transfer-Encoding: chunked < {"type":"about:blank","title":"Not Found","status":404,"detail":"No static resource repro.","instance":"/repro"}* Connection #0 to host localhost left intact
Notes
I tried various combinations to see what caused this behaviour in Spring 6.2.4
. Here are my findings:
Server | Extends OncePerRequestFilter ? |
Extends ResponseEntityExceptionHandler ? |
Output |
---|---|---|---|
Jetty | ✔ | ✔ | application/json;charset=null |
✔ | ❌ | application/json |
|
❌ | ✔ | application/problem+json |
|
❌ | ❌ | application/json |
|
Tomcat | ➖ | ➖ | application/json |
✔: this class is present in the Spring Boot app ❌: this class is not present in the Spring Boot app ➖: doesn't matter whether or not this class is present in the Spring Boot app
Comment From: isanghaessi
Hello! I'm debugging this issue now. I reproduced via @joshdcu 's demo application.
I think, this is not bug of Spring-Framework but Jetty.
In summarize,
- Jetty's ServletApiResponse has weird behavior in setting 'Content-Type'
- they have difference in DispatcherServlet.processHandlerException method Spring-Framework 6.2.5 / Spring Boot 3.4.4 and Spring-Framework 6.2.3 / Spring Boot 3.4.3
Here's details
ServletApiResponse has bug.
There are three points what changing Content-Type in providing demo application.
Spring-Framework 6.2.5 / Spring Boot 3.4.4 1. set to "application/json" in registered custom Filter.
- set to null in DispatcherServlet.processHandlerException()
- set to "application/problem+json" in Content-type negotiation by AbstractMessageConverterMethodProcessorwriteWithMessageConverters().
After that step, ServletApiResponse has "application/problem+json;charset=null" state.
I doubt weird behavior in ServletApiResponse.
So, I tested by Filter by two ways.
- use setHeader method.
- use setContentType method.
- When Using Jetty
using setHeader method.
using setContentType method.
The results are same and shows weird response. I can't find out the rule but it seems having problem.
- When Using Tomcat
using setHeader method.
using setContentType method.
Tomcat has no weird behavior. but, contentType can't be set by setHeaderMethod but only set by setContentType method.
Difference between Spring-Framework 6.2.5 / Spring Boot 3.4.4 and Spring-Framework 6.2.3 / Spring Boot 3.4.3
- DispatcherServlet.processHandlerException [Spring-Framework 6.2.5 / Spring Boot 3.4.4]
- DispatcherServlet.processHandlerException [Spring-Framework 6.2.3 / Spring Boot 3.4.3]
It's because @joshdcu noticed problem upgrading Spring-Framework / Spring Boot version.
Conclusion
Jetty's ServletApiResponse has bug in setting Content-Type. Spring-Framework 6.2.5 / Spring Boot 3.4.4 and Spring-Framework 6.2.3 / Spring Boot 3.4.3 has difference in handling exception. So, Spring Application works differently.
And here's my opinion. - we can bypass this bug by edit MediaType class by adding exception logic if charset is null. - replace using setHeader("Content-Type", "SOME_CONTENT_TYPE") to setContentType("SOME_CONTENT_TYPE") in Srping-Framework.
I tried to figure out the cause of this issue, and I think this is a bug.
If this issue is judged to be a bug, I would like to discuss and solve the solution together!
🙇♂️🙇♂️🙇♂️
Comment From: isanghaessi
I made a issue in jetty repo.
https://github.com/jetty/jetty.project/issues/13268
Comment From: joshdcu
Thanks for the great debugging, @isanghaessi!
Comment From: bclozel
Closing in favor of the Jetty issue. Thanks all for the feedback and investigation.
For reference, this behavior was introduced in https://github.com/spring-projects/spring-framework/issues/34366. If we need to further adapt we can reopen this issue.