4 mins read
Why does Spring Boot return a 500 Internal Error status instead of 404 after an update
Understanding why after updating Spring to version 6.1+ in a Java application, a 500 Internal Error occurs instead of the expected 404 Not Found for non-existent API calls and how to fix it.
Intro
In this note, we will discuss why a 500 Internal Error occurs instead of the expected 404 Not Found after updating Spring or Spring Boot in a Java application and how to fix it. This article addresses errors when updating to Spring 6.1+ and Spring Boot 3.2+ from earlier versions.
What could go wrong?
After updating the version of Spring or Spring Boot in your application, you may notice that when requesting a non-existent resource, a 500 Internal Error is returned instead of the expected 404 status as it was before:
{
"timestamp": "2024-10-22T12:34:56.789Z",
"status": 404,
"error": "Not Found",
"path": "/adabracadabra"
}
In Spring 6.1+ and Spring Boot 3.2+, a change was made to the behavior of handling the NoResourceFoundException
in the ResourceHttpRequestHandler
class.
Now this exception is handled not by Spring, but by the error handler defined by the developer.
A standard practice is to use the @ExceptionHandler
annotation to handle exceptions in controllers, and in case of an unexpected error, return a 500 status.
For example, in your code, you might have a default handler like this:
@ExceptionHandler(Exception.class)
public ResponseEntity handleAll(Exception ex, WebRequest request) {
log.error(ex.getMessage(), ex);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ex.getMessage());
}
When any exception occurs in the application, this method intercepts it, logs it, and returns a response with a 500 status code and an error message.
In the new version of Spring, NoResourceFoundException
also gets caught by this handler, and instead of the standard 404, a 500 status is returned.
How to fix it?
To fix this issue, you need to add an exception handler for NoResourceFoundException
in the controller and return a 404 status, or create a separate exception handler for NoResourceFoundException
:
import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.resource.NoResourceFoundException;
import java.time.Instant;
@Slf4j
@ControllerAdvice
public class CustomExceptionHandler {
// ...
@ExceptionHandler(NoResourceFoundException.class)
public ResponseEntity<CustomErrorResource> handleNoResourceFoundException(NoResourceFoundException exception) {
log.error("Resource not found", exception);
CustomErrorResource resource = CustomErrorResource.builder()
.timestamp(Instant.now().toString())
.status(exception.getStatusCode().value())
.error(exception.getMessage())
.path(exception.getResourcePath())
.build();
return ResponseEntity
.status(exception.getStatusCode())
.body(resource);
}
@Data
@Builder
public static class CustomErrorResource {
private String timestamp;
private int status;
private String error;
private String path;
}
// ...
}
Now the error status and response body return to their previous state.
PROFIT!
Useful links
- Spring 6.1 / Spring Boot 3.2.0 Changelog
- NoResourceFoundException docs
- ExceptionHandler docs
- Spring boot 404 Not Found behavior github issue