104

I have a very simple @RestController, and I'm trying to set a custom error message. But for some reason, the message for the error is not showing up.

This is my controller:

@RestController @RequestMapping("openPharmacy") public class OpenPharmacyController { @PostMapping public String findNumberOfSurgeries(@RequestBody String skuLockRequest) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "This postcode is not valid"); } } 

This is the response that I get:

{ "timestamp": "2020-06-24T17:44:20.194+00:00", "status": 400, "error": "Bad Request", "message": "", "path": "/openPharmacy/" } 

I'm passing a JSON, but I'm not validating anything, I'm just trying to set the custom message. If I change the status code, I see that on the response, but the message is always empty.

Why is this not working like expected? This is such a simple example that I can't see what may be missing. When I debug the code I can see that the error message has all the fields set. But for some reason, the message is never set on the response.

6
  • 2
    I have the same problem, but only when I run the jar. It doesn't happen when the application is started with mvn spring-boot:run Commented Jun 26, 2020 at 9:14
  • 63
    I managed to get the message to display by putting server.error.include-message=always in application.properties as explained here. Commented Jun 26, 2020 at 9:30
  • @Hassan Please add your comment as an answer so we can upvote it :) Commented Jun 26, 2020 at 16:12
  • 2
    @Oo.oO It's cool, you can upvote the original answer so that it gains visibility Commented Jun 27, 2020 at 1:11
  • 1
    @Hassan I upvoted another random answer of yours.. Commented Sep 17, 2020 at 0:58

7 Answers 7

150

This answer was provided by the user @Hassan in the comments on the original question. I'm only posting it as an answer to give it better visibility.


Solution

Basically, all you need to do is add server.error.include-message=always to your application.properties file, and now your message field should be populated.

This behavior was changed in Spring Boot 2.3 which you can read about here:

Spring Boot 2.3 Release Notes: Changes to the Default Error Page’s Content

Sign up to request clarification or add additional context in comments.

6 Comments

I think setting server.error.include-message=always could be a problem because it als disclosure messages of internal exceptions (like IllegalStateException) !!!
@MarkusHettich I agree, that's why you should always have an exception handler that catches Exception which then converts the error message into a generic 500 response.
For security reasons, you should never activate this option in production environment to mitigate potential sensitive information disclosures.
Also note that it returns the message in the response body and only if the body can be encoded with one of the supported encoders. My request included the header Accept: application/ion and then it couldn't serialize the error anymore and it was left out. Changing the request's Accept header into Accept: application/json, application/ion fixed the issue. It returned JSON in case of error and ION otherwise.
I love Spring, but this is a blunder IMO.
|
15

I have the very same issue. If I use this construct

throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Error in update"); 

My message is not passed to client via JSON. For me, the only way to go around it was to create GlobalExceptionHandler class

package mypackage; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import java.util.Date; @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(NotFoundException.class) public ResponseEntity<ErrorDTO> generateNotFoundException(NotFoundException ex) { ErrorDTO errorDTO = new ErrorDTO(); errorDTO.setMessage(ex.getMessage()); errorDTO.setStatus(String.valueOf(ex.getStatus().value())); errorDTO.setTime(new Date().toString()); return new ResponseEntity<ErrorDTO>(errorDTO, ex.getStatus()); } } 

I have also created my own Exception type

package mypackage; import org.springframework.http.HttpStatus; public class NotFoundException extends RuntimeException { public NotFoundException(String message) { super(message); } public HttpStatus getStatus() { return HttpStatus.NOT_FOUND; } } 

With this, I am able to throw exception from the controller and I am getting proper result in JSON - the message I want to see.

@PutMapping("/data/{id}") public DataEntity updateData(@RequestBody DataEntity data, @PathVariable int id) { throw new NotFoundException("Element not found"); } 

I had to introduce ErrorDTO as well

package mypackage; public class ErrorDTO { public String status; public String message; public String time; ... ... // getters and setters are here ... ... } 

Update

As mentioned by @Hassan and @cunhaf (in comments under original question), the solution with

server.error.include-message=always 

works perfectly fine with ResponseStatusException. Still, solution with GlobalExceptionHandler might be better in case someone wants to pass more info via Exception.

Source code

Samples can be found here: Global Exception Handler

Comments

11

For those who are concerned about potentially leaking sensitive information with the application.properties recommended by the accepted answer, I found that there are additional options available for the server.error.include-message property.

Setting server.error.include-message=always will always include a reason when an exception is thrown, even if that reason potentially exposes sensitive information about your application.

A better option to use is server.error.include-message=on-param, which will only include a message when you explicitly set one via ResponseStatusException:

if (requestBody.someField == null) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "someField is required."); } 

1 Comment

this website states, that one has to add the URL-path-parameter ?message=true (which explains why on-param does not work for me)
2

Strangely, Spring Boot 2.6.x changed this behavior again and the error message set on ResponseStatusException is not returned. I had to downgrade to 2.5.6 in order to solve it. In the end I had something like this:

 @DeleteMapping("/{id}") @ResponseStatus(HttpStatus.OK) public MessageResponse deleteById(@PathVariable(value = "id") Integer id) { try { userService.deleteById(id); } catch (Exception e) { throw new ResponseStatusException(HttpStatus.EXPECTATION_FAILED, "Error deleting user. User has dependencies", e); } } 

1 Comment

2

there is bean that can be overridden to include custom message.

Normal Spring Boot: org.springframework.boot.web.servlet.error.ErrorAttributes

Spring Wedbflux: org.springframework.boot.web.reactive.error.ErrorAttributes


the default implementation is DefaultErrorAttributes.

You can override public Map<String, Object> getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {

the return make it return the message you want


In my case I created a decorator that will remove messages if it's internal server error:

public class CustomErrorAttributesDecorator implements ErrorAttributes { private final ErrorAttributes errorAttributes; CustomErrorAttributesDecorator(ErrorAttributes errorAttributes){ this.errorAttributes = errorAttributes; } @Override public Map<String, Object> getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) { Map<String, Object> errorAttributesMap = this.errorAttributes.getErrorAttributes(request, options); if(HttpStatus.INTERNAL_SERVER_ERROR.value() == (int) errorAttributesMap.get("status")){ errorAttributesMap.remove("message"); } return errorAttributesMap; } ... } 

and then I created a @Bean as follows:

 @Bean ErrorAttributes customErrorAttributes(){ return new CustomErrorAttributesDecorator(new DefaultErrorAttributes()); } 

Comments

1

Starting from the 2.3 version, Spring Boot doesn't include an error message on the default error page. The reason is to reduce the risk of leaking information to a client

To change the default behavior, we can use a server.error.include-message property.

Comments

0

if you are making a REST API take a look at zalando-problem for exception handling. problem-spring-web

Evry exception is catched by @ExceptionHandler and you just have to return a Problem object using the zalando builder.

public class ResourceNotFoundException extends Exception { // ... } @ControllerAdvice public class MyExceptionHandler implements ProblemHandling { @ExceptionHandler(ResourceNotFoundException.class) @ResponseStatus(value = HttpStatus.NOT_FOUND) public ResponseEntity<Problem> handleResourceNotFoundException(final ResourceNotFoundException exception, final NativeWebRequest request) { Problem problem = Problem.builder()// .withType(URI.create("https://example.com/problem/not-found"))// .withTitle("Resource not found")// .withStatus(Status.NOT_FOUND)// .withDetail(exception.getMessage()).build(); } ... } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.