Exception handling is an essential part of building robust applications in Spring Boot. However, developers often face issues like uncaught exceptions, improper error messages, and inconsistent responses. In this guide, Iβll walk you through the most common exception handling problems and how to solve them effectively.
πΉ Problem 1: Returning Generic Error Messages
By default, Spring Boot throws generic error messages, which donβt provide meaningful information to the client.
β Bad Practice:
If an exception occurs, the response might look like this:
{
"timestamp": "2025-04-06T14:22:00.123+00:00",
"status": 500,
"error": "Internal Server Error",
"message": "No message available"
}
This is not helpful for debugging.
β
Solution: Use @ControllerAdvice for Global Exception Handling
Instead of returning generic errors, create a custom global exception handler:
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<Object> handleGlobalException(Exception ex) {
Map<String, Object> errorDetails = new HashMap<>();
errorDetails.put("timestamp", LocalDateTime.now());
errorDetails.put("message", ex.getMessage());
errorDetails.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value());
return new ResponseEntity<>(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Now, the error response is more meaningful and structured.
πΉ Problem 2: Handling Specific Exceptions Like EntityNotFoundException
Sometimes, fetching a resource that doesnβt exist causes 500 Internal Server Error instead of a 404 Not Found.
β Bad Practice:
If an entity is not found, it throws:
javax.persistence.EntityNotFoundException: No entity found for query
β
Solution: Handle Specific Exceptions
Modify the global exception handler to return 404 Not Found when an entity is missing:
@ExceptionHandler(EntityNotFoundException.class)
public ResponseEntity<Object> handleEntityNotFoundException(EntityNotFoundException ex) {
Map<String, Object> errorDetails = new HashMap<>();
errorDetails.put("timestamp", LocalDateTime.now());
errorDetails.put("message", ex.getMessage());
errorDetails.put("status", HttpStatus.NOT_FOUND.value());
return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
}
Now, the API will return:
{
"timestamp": "2025-04-06T14:30:00.123+00:00",
"message": "User not found",
"status": 404
}
πΉ Problem 3: Handling Validation Errors in @RequestBody
If the user submits invalid data in a POST request, Spring Boot might throw MethodArgumentNotValidException with an unclear error message.
β
Solution: Customize Validation Error Handling
Modify your GlobalExceptionHandler to catch validation errors:
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Object> handleValidationException(MethodArgumentNotValidException ex) {
Map<String, Object> errors = new HashMap<>();
for (FieldError fieldError : ex.getBindingResult().getFieldErrors()) {
errors.put(fieldError.getField(), fieldError.getDefaultMessage());
}
return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
}
Now, if a user submits invalid data, the response will be more readable:
{
"name": "Name is required",
"email": "Invalid email format"
}
πΉ Problem 4: Handling Custom Business Logic Exceptions
Letβs say we have a banking application, and a user tries to withdraw more money than available. Instead of throwing a generic RuntimeException, we can create a custom exception.
β
Solution: Create a Custom Exception Class
public class InsufficientBalanceException extends RuntimeException {
public InsufficientBalanceException(String message) {
super(message);
}
}
Now, handle it in GlobalExceptionHandler:
@ExceptionHandler(InsufficientBalanceException.class)
public ResponseEntity<Object> handleInsufficientBalance(InsufficientBalanceException ex) {
Map<String, Object> errorDetails = new HashMap<>();
errorDetails.put("timestamp", LocalDateTime.now());
errorDetails.put("message", ex.getMessage());
errorDetails.put("status", HttpStatus.BAD_REQUEST.value());
return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST);
}
If a user tries to withdraw more than their balance, they will see this error:
{
"timestamp": "2025-04-06T14:45:00.123+00:00",
"message": "Insufficient balance for withdrawal",
"status": 400
}
π― Conclusion
πΉ Key Takeaways:
β
Use @ControllerAdvice for centralized exception handling.
β
Handle specific exceptions like EntityNotFoundException properly.
β
Improve validation error messages for better user experience.
β
Create custom exceptions for business logic errors.
By implementing these best practices, youβll ensure your Spring Boot application is robust, user-friendly, and easy to debug. π
π¬ Whatβs Next?
What are some common Spring Boot exception handling issues you've faced? Drop a comment below!
π If you found this guide helpful, check out my previous article: Getting Started with Spring Boot and PostgreSQL
π Follow me for more backend development tips!
Top comments (0)