GlobalExceptionHandler always returns 500

Viewed 2814

I have following Exception Handler:

@Log4j2
@ControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(Exception.class)
public ResponseEntity handleException(Exception e) {
    logExceptionWithPath("Unhandled general exception", e);
    return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}

@ResponseBody
@ExceptionHandler({ValidationException.class})
public ResponseEntity<ErrorResponse> handleBindException(ValidationException ex) {
    logExceptionWithPath("Validation exception", ex);
    return new ResponseEntity<>(new ErrorResponse(ex.getMessage()), HttpStatus.BAD_REQUEST);
}


@ResponseBody
@ExceptionHandler({BindException.class})
public ResponseEntity<ErrorResponse> handleBindException(BindException ex) {
    logExceptionWithPath("Bind exception", ex);
    return new ResponseEntity<>(new ErrorResponse(ex.getMessage()), HttpStatus.BAD_REQUEST);
}

@ResponseBody
@ExceptionHandler(AmbiguousTermException.class)
public ResponseEntity<ErrorResponse> handleAmbiguousTermException(AmbiguousTermException ex) {
    logExceptionWithPath("AmbiguousTermException", ex);
    return new ResponseEntity<>(new ErrorResponse(ex.getMessage()), HttpStatus.BAD_REQUEST);
}

This code should wrap exceptions and return ResponseEntity with correct Status code.

But instead, I always get this:

{
"timestamp": 1560445348350,
"status": 500,
"error": "Internal Server Error",
"message": "I/O error on POST request for \"******": Error code:400 Message: ; nested exception is java.io.IOException: Error code:400 Message: ",
"path": "*****"
}

In logs I have always messages produced by logExceptionWithPath, so it seems like ExceptionHandler is working fine, but then there is IOException somewhere.

This response is similar for each exception (and each @ExceptionHandler)

below few logs from my app and spring:

2019-06-13 17:26:00,237 DEBUG *:102 - assertTermsUnequivocal
2019-06-13 17:26:00,237 DEBUG org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver:403 - Using @ExceptionHandler public org.springframework.http.ResponseEntity<*.ErrorResponse> *.GlobalExceptionHandler.handleAmbiguousTermException(*.AmbiguousTermException)
2019-06-13 17:26:00,238 ERROR *.GlobalExceptionHandler:92 - AmbiguousTermException for path: http://*/search
here stack trace from AmbiguousTermException
2019-06-13 17:26:00,239 DEBUG org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor:268 - Using 'application/json', given [text/plain, application/json, application/cbor, application/*+json, */*] and supported [application/json, application/*+json, application/json, application/*+json, application/cbor]
2019-06-13 17:26:00,239 DEBUG org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor:90 - Writing [*.ErrorResponse@17bd7832]
2019-06-13 17:26:00,240 DEBUG org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver:143 - Resolved [*.AmbiguousTermException: terms are ambiguous]
2019-06-13 17:26:00,240 DEBUG org.springframework.web.servlet.DispatcherServlet:1130 - Completed 400 BAD_REQUEST

That's all from interesting logs. Maybe this is not connected to Spring at all, but rather servers it is running? Anyway if you have any clues why it don't work, please leave an answer or comment.

2 Answers

You have to add Response status to the method like @ResponseStatus(HttpStatus.NOT_FOUND)

@ResponseBody
@ResponseStatus(HttpStatus.BAD_REQUEST) // Added this annotation
@ExceptionHandler(AmbiguousTermException.class)
public ResponseEntity<ErrorResponse> handleAmbiguousTermException(AmbiguousTermException ex) {
    logExceptionWithPath("AmbiguousTermException", ex);
    return new ResponseEntity<>(new ErrorResponse(ex.getMessage()), HttpStatus.BAD_REQUEST);
}

This is only if the above solution does not work. If you already checked your GlobalExceptionHandler and everything seems ok.

As for my case, everything is working as expected for the other exceptions. I have a single handler method for different exceptions that I want to return a bad request status. All of the exceptions are behaving as expected, except for one.

It happened that this weird exception was extended to Exception, and Exception was handled to return an error 500.

To solve it, I just extend it to RuntimeException instead of Exception since my RuntimeException was configured to return a bad request status.

To be clear, please see the sample below:

GlobalExceptionHandler .java

@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

    ...

    @ExceptionHandler(Exception.class)
    public final ResponseEntity<ErrorResponse> handleAllExceptions(Exception ex, HttpServletRequest request) {
        printErrorLogs(ex, request, HttpStatus.INTERNAL_SERVER_ERROR);
        return getErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, ex);
    }

    @ExceptionHandler({CustomException1.class, CustomException2.class, CustomException3.class})
    public final ResponseEntity<ErrorResponse> handleCustomRuntimeException(RuntimeException ex, HttpServletRequest request) {
        printErrorLogs(ex, request, HttpStatus.BAD_REQUEST);
        return processErrorResponse(ex.getMessage());
    }

    ...

}

CustomException1.java

public class CustomException1 extends RuntimeException {
    private static final long serialVersionUID = 1L;

    public CustomException1 () {
        super();
    }

    public CustomException1 (String message) {
        super(message);
    }

    public CustomException1 (String message, Throwable cause) {
        super(message, cause);
    }

    public CustomException1 (Throwable cause) {
        super(cause);
    }

    protected CustomException1 (String message, Throwable cause, boolean enableSuppression, boolean
            writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

I just change the custom exception parent from Exception to RuntimeException.

For my case, I know that my GlobalExceptionHandler is working except for specific exception(s). Upon checking, this problematic custom exception was extended from Exception which already have a handler that returns an error 500. In order to handle the error and return a bad request status, I will extend it to RuntimeException since I handled this exception to return a bad request status.

Again, this solution is only for this case. You just need to check the exception handler for the exception you want to check/handle/customize handling.

Going back to your problem, It was not shown how AmbigousTermException was defined. I am thinking that its parent is Exception and you have a handler for exception that returns error 500.

That is why you always get the error 500 status.

Related