Summary
Since upgrading to Spring Boot 3.4.1, we have observed unexpected behavior where HTTP headers with names matching @JsonProperty
annotations on POJO fields are being automatically deserialized into those fields. This behavior was not present in earlier versions.
Observations
- Affected Behavior:
- An HTTP request with a
priority
header results in deserialization into theTaskQueryFilterParameter.priorityIn
field, which has the annotation@JsonProperty("priority")
. -
Similarly, a
planned
header is deserialized into theTaskQueryFilterParameter.plannedWithin
field, annotated with@JsonProperty("planned")
. -
Reproducibility:
-
This behavior occurs whenever request headers match the names used in
@JsonProperty
annotations on request object fields. -
Impact:
-
This behavior can lead to unintended data mapping, affecting applications that rely on these headers for different purposes.
-
Analysis Results:
- After reviewing the dependencies and updates introduced with Spring Boot 3.4.1, we were unable to identify the specific cause (e.g., a library or code change).
- It is unclear whether this is an intentional feature or a regression.
Expected Behavior
It is unclear whether HTTP headers with names matching @JsonProperty
annotations should be automatically deserialized into POJO fields. If this is the intended behavior, guidance on how to configure or disable it would be helpful.
Actual Behavior
HTTP headers with names matching @JsonProperty
annotations are deserialized into the respective POJO fields, even without explicit configuration.
Questions
- Is this behavior an intentional change in Spring Boot 3.4.1?
- If so, what is its purpose, and how can it be disabled if undesired?
- If this is a bug, is there a planned fix or workaround?
Thank you for your time and assistance! 😊
Comment From: bclozel
This is a new feature introduced in Spring Framework 6.2.
We have refined this behavior in 6.2.2. The priority header should not be bound anymore. As for the "planned" header, I've never seen this header, is this something common? In any case, you can get more control over the header binding with the following: https://github.com/spring-projects/spring-framework/issues/34182#issuecomment-2575598084
This week's Spring Boot release will use this version, but you can already upgrade in your build with the "spring-framework.version" property.
Comment From: jannikFuellgrafEnvite
Thank you for the quick and detailed response! 😊
Regarding the “planned” header, it is not a standard header but rather a custom one we introduced specifically to test and confirm the behavior alongside the “priority” header. Its sole purpose was to validate that headers matching @JsonProperty annotations are consistently being bound, regardless of whether they are commonly used or not.
Comment From: bclozel
Alright thanks for the feedback. I'll close this issue now, since the situation is already resolved for "priority" and that apps can opt-out of this feature for all other headers as well.
Thanks for reaching out!
Comment From: jannikFuellgrafEnvite
Dear @bclozel,
Unfortunately, the issue has not been resolved yet in our setup. I am attaching the error message below, which indicates that the priority header is still being applied. Is there a specific configuration that needs to be considered to achieve the expected behavior?
Since I do not have the necessary permissions to reopen the ticket, I hope this comment reaches you and that we can still solve the issue.
org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [1] in public org.springframework.http.ResponseEntity<io.kadai.task.rest.models.TaskSummaryPagedRepresentationModel> io.kadai.task.rest.TaskController.getTasks(jakarta.servlet.http.HttpServletRequest,io.kadai.task.rest.TaskQueryFilterParameter,io.kadai.task.rest.TaskQueryFilterCustomFields,io.kadai.task.rest.TaskQueryFilterCustomIntFields,io.kadai.task.rest.TaskQueryGroupByParameter,io.kadai.task.rest.TaskController$TaskQuerySortParameter,io.kadai.common.rest.QueryPagingParameter<io.kadai.task.api.models.TaskSummary, io.kadai.task.api.TaskQuery>): [Field error in object 'taskQueryFilterParameter' on field 'priority': rejected value [u=3, i]; codes [typeMismatch.taskQueryFilterParameter.priority,typeMismatch.priority,typeMismatch.[I,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [taskQueryFilterParameter.priority,priority]; arguments []; default message [priority]]; default message [Failed to convert value of type 'java.lang.String' to required type 'int[]'; For input string: "u=3,i"]]
Comment From: bclozel
Can you point me to something I can run in order to reproduce the issue?
Comment From: jannikFuellgrafEnvite
Hi @bclozel,
Thanks for looking into this!
Here is the Pull Request with the update to Spring Boot 3.4.2:
🔗 PR #478 - Update to Spring Boot 3.4.2
To check out the code and run the application, follow these steps:
git clone https://github.com/kadai-io/kadai.git
gh pr checkout 478
cd kadai
./mvnw clean install -DskipTests
./mvnw spring-boot:run -pl :kadai-rest-spring-example-boot
This will start our application. Navigate to http://localhost:8080/kadai/login
About Kadai
We are building an open-source enterprise task management solution called Kadai.
Issue Details
The error occurs when using Safari and Firefox.
Steps to Reproduce 1. Click on the Burger Menu in the top-left corner. 2. Navigate to Workplace. 3. Switch between Workbaskets and Task Search.
At this point, an error should appear, caused by the priority header being applied.
Let us know if you need any further details! We appreciate your help in resolving this issue.
Comment From: bclozel
What am I supposed to use as credentials? I have tried "admin/admin" and then I'm getting a 404 with
{
"type" : "about:blank",
"title" : "Not Found",
"status" : 404,
"detail" : "No static resource index.html.",
"instance" : "/kadai/index.html"
}
Maybe it would be easier to share a minimal sample created with start.spring.io and a simple controller that replicates what you're binding to?
Comment From: jannikFuellgrafEnvite
Sorry, I'm still new to the project.
You are correct about the credentials: admin/admin.
The correct steps before running the application are:
cd kadai
cd web
yarn install
yarn build:prod
cd ..
./mvnw clean install -DskipTests
./mvnw spring-boot:run -pl :kadai-rest-spring-example-boot
````
If that still doesn’t work, I will create a minimal example to improve reproducibility.
**Comment From: bclozel**
Thanks for your help @jannikFuellgrafEnvite , I managed to reproduce this with a simple setup:
```java
@RestController
public class TestController {
@GetMapping("/tasks")
public TaskQueryFilterParameter getTasks(TaskQueryFilterParameter taskQueryFilterParameter) {
return taskQueryFilterParameter;
}
record TaskQueryFilterParameter(int[] priority) {
}
}
$ http :8080/tasks 'Priority:u=0'
{
"error": "Bad Request",
"path": "/tasks",
"status": 400,
"timestamp": "2025-01-29T15:24:33.276+00:00"
}
The fix should be available in 6.2.3-SNAPSHOT in a few minutes. In the meantime you can apply this workaround to your application.
Cheers!
Comment From: jannikFuellgrafEnvite
Thank you @bclozel ! :-)