Clean • Professional
Exception handling in Spring Boot helps your application respond properly when something goes wrong, instead of crashing or returning confusing error pages.
It is extremely important for REST APIs, where clients expect clear, structured, and meaningful error responses.
Exception Handling is the process of handling runtime errors that occur while an application is running.
Instead of letting the application fail, exception handling allows you to:
In Spring Boot REST APIs, exception handling ensures that clients always receive a clean JSON response, even when errors occur.
Exception handling is important because it:
Without proper exception handling, Spring Boot may return confusing responses that are difficult for frontend or API consumers to understand.
Exception handling ensures all these cases are handled gracefully.
When an exception occurs in a Spring Boot application:

@ExceptionHandler@ControllerAdviceSpring Boot provides multiple ways to handle exceptions.
This is the simplest way to handle exceptions.
The exception is handled inside the controller method itself.
Example
@GetMapping("/users/{id}")
public ResponseEntity<User>getUser(@PathVariableint id) {
try {
Useruser= userService.findById(id);
return ResponseEntity.ok(user);
}catch (UserNotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
}
}
Drawbacks
👉 This approach is not recommended for real-world REST APIs.
@ExceptionHandler in ControllerIn this approach, exception handling logic is written in a separate method inside the same controller.
Example
@RestController
@RequestMapping("/users")
publicclassUserController {
@GetMapping("/{id}")
public UsergetUser(@PathVariableint id) {
return userService.findById(id);
}
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<String>handleUserNotFound(UserNotFoundException ex) {
return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.body(ex.getMessage());
}
}
Limitations
@ControllerAdvice / @RestControllerAdvice – Global Exception HandlingThis is the most important and recommended approach.
It allows you to handle exceptions globally for all controllers from one place.
For REST APIs, always use:
@RestControllerAdvice
Example
@RestControllerAdvice
publicclassGlobalExceptionHandler {
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<String>handleUserNotFound(UserNotFoundException ex) {
return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.body(ex.getMessage());
}
@ExceptionHandler(Exception.class)
public ResponseEntity<String>handleAllExceptions(Exception ex) {
return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Something went wrong");
}
}
Benefits
Returning plain text is not ideal for APIs.
Instead, return structured JSON responses.
Error Response Class
publicclassErrorResponse {
private String message;
privateint status;
privatelong timestamp;
publicErrorResponse(String message, int status) {
this.message = message;
this.status = status;
this.timestamp = System.currentTimeMillis();
}
// getters and setters
}
Using Error Response in Exception Handler
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<ErrorResponse>handleUserNotFound(UserNotFoundException ex) {
ErrorResponseerror=
newErrorResponse(ex.getMessage(), HttpStatus.NOT_FOUND.value());
returnnewResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
Example JSON Response
{
"message":"User not found",
"status":404,
"timestamp":1672567890123
}
When using Bean Validation (@Valid or @Validated), Spring Boot automatically validates request data.
If validation fails:
MethodArgumentNotValidExceptionExample DTO with Validation
publicclassUserRequest {
@NotBlank(message = "Name is required")
private String name;
@Email(message = "Email should be valid")
private String email;
@Min(value = 18, message = "Age must be at least 18")
privateint age;
}
Controller Example
@PostMapping("/users")
public ResponseEntity<String>createUser(@Valid@RequestBody UserRequest request) {
return ResponseEntity.ok("User created successfully");
}
@RestControllerAdvice
publicclassGlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, Object>>handleValidationErrors(
MethodArgumentNotValidException ex) {
List<String> errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(error -> error.getField() +": " + error.getDefaultMessage())
.toList();
Map<String, Object> response =newHashMap<>();
response.put("timestamp", System.currentTimeMillis());
response.put("status", HttpStatus.BAD_REQUEST.value());
response.put("errors", errors);
return ResponseEntity.badRequest().body(response);
}
}
Example Validation Error Response
{
"timestamp":1672567890123,
"status":400,
"errors":[
"name: Name is required",
"email: Email should be valid",
"age: Age must be at least 18"
]
}
@ResponseStatus on Custom ExceptionYou can define HTTP status directly on a custom exception.
@ResponseStatus(HttpStatus.NOT_FOUND)
publicclassUserNotFoundExceptionextendsRuntimeException {
publicUserNotFoundException(String message) {
super(message);
}
}
Spring automatically returns 404 when this exception is thrown.
ResponseStatusExceptionSpring Boot also allows returning HTTP status directly.
@GetMapping("/{id}")
public UsergetUser(@PathVariableint id) {
return userRepository.findById(id)
.orElseThrow(() ->
newResponseStatusException(
HttpStatus.NOT_FOUND,
"User not found"
));
}
This approach is useful for simple cases, but global handling is better for large projects.
ProblemDetail (Modern Approach)Spring Boot 3 introduced ProblemDetail for standardized error responses.
@ExceptionHandler(UserNotFoundException.class)
public ProblemDetailhandleUserNotFound(UserNotFoundException ex) {
ProblemDetailproblem=
ProblemDetail.forStatus(HttpStatus.NOT_FOUND);
problem.setTitle("User Not Found");
problem.setDetail(ex.getMessage());
return problem;
}
Exception handling is a core part of building reliable Spring Boot REST APIs.
Spring Boot provides multiple ways to handle exceptions:
@ExceptionHandler (controller-level)@ControllerAdvice / @RestControllerAdvice