Spring Boot
Article 11: Exception Handling the Right Way in Spring Boot
By Utility Zone · 2026-01-27T18:29:36.852375
1. Introduction
Errors are inevitable.
What separates professional APIs from amateur ones is:
- How errors are handled
- How clearly errors are communicated to clients
Spring Boot provides powerful mechanisms to handle exceptions centrally and cleanly.
2. The Problem Without Proper Exception Handling
Without proper handling:
- Stack traces leak to clients
- Inconsistent error responses
- Duplicate try-catch blocks everywhere
Example (❌ bad practice):
try {
// business logic
} catch (Exception e) {
return "Something went wrong";
}
This approach:
- Hides real issues
- Is hard to maintain
- Produces poor APIs
3. Common Exceptions in REST APIs
Typical exceptions:
EntityNotFoundExceptionIllegalArgumentExceptionMethodArgumentNotValidExceptionNullPointerException(should be avoided)
We must map these to proper HTTP responses.
4. Handling Exceptions at Controller Level
4.1 Using @ExceptionHandler
@RestController
public class SampleController {
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<String> handleIllegalArgument(IllegalArgumentException ex) {
return ResponseEntity.badRequest().body(ex.getMessage());
}
}
✔ Works
❌ Not scalable (controller-specific)
5. Global Exception Handling with @ControllerAdvice
5.1 What Is @ControllerAdvice?
It allows:
- Centralized exception handling
- Reusable error responses
- Clean controllers
5.2 Creating a Global Exception Handler
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(EntityNotFoundException.class)
public ResponseEntity<String> handleNotFound(EntityNotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(ex.getMessage());
}
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleGeneric(Exception ex) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Internal server error");
}
}
6. Handling Validation Errors Properly
Spring throws:
MethodArgumentNotValidException
Handle it globally:
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidation(
MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage())
);
return ResponseEntity.badRequest().body(errors);
}
Produces clean, structured errors.
7. Creating Custom Exceptions
7.1 Example: ResourceNotFoundException
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
Throw it from service layer:
throw new ResourceNotFoundException("User not found");
8. Standard Error Response Structure
Professional APIs return consistent errors:
{
"timestamp": "2026-01-01T10:15:30",
"status": 404,
"error": "Not Found",
"message": "User not found",
"path": "/users/10"
}
Consistency matters for frontend & clients.
9. Best Practices
✔ Handle exceptions globally
✔ Don’t expose stack traces
✔ Use meaningful HTTP status codes
✔ Throw exceptions from service layer
✔ Keep controllers clean
10. Common Beginner Mistakes
❌ Catching Exception everywhere
❌ Returning 200 OK for errors
❌ Exposing internal messages
❌ Mixing validation & business errors
11. What You Should Understand Before Moving On
You should now know:
- Why global exception handling is important
- How
@ControllerAdviceworks - How to handle validation & business exceptions
- How to return clean error responses
12. What’s Next?
➡ Article 12: API Documentation with Swagger / OpenAPI
- Swagger UI
- API contracts
- Developer-friendly APIs
Type Next when you’re ready.