Spring Boot @ControllerAdvice partially working, not working for custom exception

Issue

I’m having a peculiar situation with my @ControllerAdvice annotated ExceptionHandler in Spring Boot 1.5.3. It catches any exceptions default Exceptions, but if I throw a custom exception it does not fire.

The ExceptionHandler:

@ControllerAdvice
public class ResponseEntityExceptionHandler {

@ExceptionHandler({ HttpMessageNotReadableException.class })
protected ResponseEntity<ErrorModel> handleInvalidJson(RuntimeException e, WebRequest request) {
    return new ResponseEntity<ErrorModel>(new ErrorModel().message("Could not parse JSON."), HttpStatus.BAD_REQUEST);
}

@ExceptionHandler({ NumberFormatException.class })
protected ResponseEntity<ErrorModel> handleInvalidRequest(RuntimeException e, WebRequest request) {
    return new ResponseEntity<ErrorModel>(new ErrorModel().message("Invalid request parameter."), HttpStatus.BAD_REQUEST);
}

@ExceptionHandler({ CannotCreateTransactionException.class })
protected ResponseEntity<ErrorModel> handleTransactionCreationException(RuntimeException e, WebRequest request) {
    return new ResponseEntity<ErrorModel>(new ErrorModel().message("Error connecting to the database, please make sure it is still available."), HttpStatus.BAD_REQUEST);
}

@ExceptionHandler({ NotFoundException.class })
protected ResponseEntity<ErrorModel> handleApiException(RuntimeException e, WebRequest request) {
    return new ResponseEntity<ErrorModel>(new ErrorModel().message(e.getMessage()), HttpStatus.NOT_FOUND);
}
}

The top 3 Exceptions all get caught and handled as they are supposed to, but the bottom Exception gets handled by the default Spring-Boot ExceptionHandler. It is a custom Exception that I throw inside a Controller:

public ResponseEntity<?> deleteActor(@ApiParam(value = "Used to identify a single actor.", required = true) @PathVariable("actor_id") Integer actorId, @RequestHeader("Accept") String accept) throws Exception {
Actor actor = actorRepository.findOne(actorId);
if (actor == null) {
    throw new NotFoundException(404, "Not found");
}
actorRepository.delete(actorId);
return new ResponseEntity<Void>(HttpStatus.NO_CONTENT);
}

I’ve tried throwing one of the top Exceptions like this:

public ResponseEntity<?> readActor(@ApiParam(value = "Used to identify a single actor.", required = true) @PathVariable("actor_id") Integer actorId, @RequestHeader("Accept") String accept) throws Exception {
    Actor actor = actorRepository.findOne(actorId);
    if (actor == null) {
        throw new NumberFormatException("");
    }
    return new ResponseEntity<Actor>(actor, HttpStatus.OK);
}

and these get handled just fine…

The tomcat logs also show this:

2017-06-05 11:30:20.080  INFO 9076 --- [           main] .m.m.a.ExceptionHandlerExceptionResolver : Detected @ExceptionHandler methods in responseEntityExceptionHandler

The Exception:

public class NotFoundException extends ApiException {
private int code;
public NotFoundException (int code, String msg) {
    super(code, msg);
    this.code = code;
}
}

The exception inherits from this baseclass:

public class ApiException extends Exception{
private int code;
public ApiException (int code, String msg) {
    super(msg);
    this.code = code;
}
}

Any ideas about why the custom Exception avoids detection by the ExceptionHandler?

I would be happy to provide additional information should that be necessary.

Solution

For this particular case the answer is to use Exception instead of RuntimeException, since NotFoundException does only inherit from Exception.

Further notable things:

  • To catch all exceptions one can use an @ExceptionHandler(Exception.class)

  • If using common names for exceptions, always check if you have imported the right one.

Answered By – rainerhahnekamp

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published