Since Boot 2.3 binding errors are no longer included in the default error page by default.
The resolution, until this commit, was to set this property accordingly.
server.error.include-binding-errors=ALWAYS
Unfortunately this no longer works in Boot 3.2.6+.
Full repro here
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
System.setProperty("server.error.include-message", "ALWAYS");
System.setProperty("server.error.include-binding-errors", "ALWAYS");
SpringApplication.run(DemoApplication.class, args);
}
@RestController
public static class Controller {
@GetMapping("/manualBindError")
public Mono<ResponseEntity<String>> repro() {
BindException bindException = new BindException(new RuntimeException("error"), "oh oh");
ObjectError objectError = new ObjectError("oh oh", new String[] {"CODE"}, null, "MESSAGE");
bindException.addError(objectError);
throw new ResponseStatusException(400, "a reason", bindException);
}
}
}
When we hit http://localhost:8080/manualBindError
Something resembling the below - with errors intact.
{
"timestamp": "2024-08-21T13:20:39.377+00:00",
"path": "/manualBindError",
"status": 400,
"error": "Bad Request",
"message": "a reason",
"requestId": "d4b54677-3",
"errors": [
{
"codes": [
"CODE"
],
"arguments": null,
"defaultMessage": "MESSAGE",
"objectName": "oh oh",
"code": "CODE"
}
]
}
Errors are missing.
{
"timestamp": "2024-08-21T13:27:27.504+00:00",
"path": "/manualBindError",
"status": 400,
"error": "Bad Request",
"requestId": "ae84da6d-1",
"message": "a reason"
}
The code below was removed from DefaultErrorAttributes
.
private Throwable determineException(Throwable error) {
if (error instanceof ResponseStatusException) {
return (error.getCause() != null) ? error.getCause() : error;
}
return error;
}
This is problematic if the cause is an instance of BindingResult
as this is no longer true, so the errors aren't added regardless of property value....
private void handleException(Map<String, Object> errorAttributes, Throwable error,
MergedAnnotation<ResponseStatus> responseStatusAnnotation, boolean includeStackTrace) {
Throwable exception;
if (error instanceof BindingResult bindingResult) {
errorAttributes.put("message", error.getMessage());
errorAttributes.put("errors", bindingResult.getAllErrors());
exception = error;
}
I can fix this by exposing a custom ErrorAttributes
Bean in each app. However, is this the intention or is this a bug? If it's the former then a documentation update would be handy...