Skip to content

When using WebFlux, server.error.include-binding-errors=ALWAYS no longer has an effect when the BindingResult exception is the cause of a ResponseStatusException #41984

@adamsmith118

Description

@adamsmith118

Background

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

Expected Behaviour/Repro

Full repro here

Code

@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

Expected

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" } ] }

Actual

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" }

Root Cause

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 was 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;	}

Resolution?

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 as this is a breaking change.

Metadata

Metadata

Assignees

Labels

type: regressionA regression from a previous release

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions