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

Closed
@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