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.

Image

  1. set to null in DispatcherServlet.processHandlerException()

Image

  1. 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.

Image

  • use setContentType method.

Image

  1. When Using Jetty

using setHeader method.

Image

using setContentType method.

Image

The results are same and shows weird response. I can't find out the rule but it seems having problem.

Image

  1. When Using Tomcat

    using setHeader method.

Image

using setContentType method.

Image

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]

Image

  • DispatcherServlet.processHandlerException [Spring-Framework 6.2.3 / Spring Boot 3.4.3]

Image

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.