Exception Handling

Build production-grade exception handling in Spring Boot — @ControllerAdvice, @ExceptionHandler, ProblemDetail, and consistent error responses across your entire API.

Last reviewed: January 2025

·Spring Boot 2.7Spring Boot 3.x
✅
Quick Understanding: @ControllerAdvice is a global exception handler — it catches exceptions thrown anywhere in your controllers and converts them to proper HTTP responses. Without it, Spring returns ugly default error pages. With it, every exception maps to a clean JSON error response with the right HTTP status code.

🔍 What Is It?

Spring's exception handling mechanism lets you centralize error handling in one place.

Key components:

  • →@ControllerAdvice / @RestControllerAdvice — global handler applied to all controllers
  • →@ExceptionHandler(ExceptionType.class) — handles a specific exception type
  • →ResponseEntityExceptionHandler — base class that handles Spring MVC exceptions
  • →ProblemDetail (Spring Boot 3.x) — RFC 7807 standardized error format

Exception hierarchy to handle:

  • →Your custom exceptions (ResourceNotFoundException, BusinessException)
  • →Spring validation: MethodArgumentNotValidException (from @Valid)
  • →Spring MVC: MethodArgumentTypeMismatchException, MissingServletRequestParameterException
  • →Spring Security: AccessDeniedException, AuthenticationException
  • →Hibernate: ConstraintViolationException, DataIntegrityViolationException

🎯 Why Do We Need It?

Without global exception handling:

  • →Unhandled exceptions return 500 Internal Server Error with a stack trace (security risk)
  • →Different parts of the API return different error formats
  • →Clients can't reliably parse error responses
  • →Debugging is harder because errors aren't logged consistently

With @ControllerAdvice:

  • →Consistent JSON error format across all endpoints
  • →Correct HTTP status codes (400 for validation, 404 for not found, 409 for conflicts)
  • →No stack traces in responses (security)
  • →Centralized logging of errors

🏗️ What Happens in Real Projects?

Every production Spring Boot API has a global exception handler. Standard pattern:

  • →ResourceNotFoundException → 404 Not Found
  • →ValidationException → 400 Bad Request with field errors
  • →BusinessException → 422 Unprocessable Entity
  • →AccessDeniedException → 403 Forbidden
  • →Exception (catch-all) → 500 Internal Server Error (log it, don't expose details)

⚙️ How It Works

When an exception is thrown from a controller (or service/repository called by a controller):

  1. Spring's DispatcherServlet catches it
  2. Looks for a matching @ExceptionHandler in the current controller first
  3. Then looks in @ControllerAdvice classes
  4. The first matching handler handles it
  5. Returns the ResponseEntity from the handler method

💻 Example

Production Exception Handling — Complete global exception handler with consistent error responses.

java
// Base exception
public class ApiException extends RuntimeException {
    private final HttpStatus status;
    private final String errorCode;

    public ApiException(HttpStatus status, String errorCode, String message) {
        super(message);
        this.status = status;
        this.errorCode = errorCode;
    }

    public HttpStatus getStatus() { return status; }
    public String getErrorCode() { return errorCode; }
}

// Specific exceptions
public class ResourceNotFoundException extends ApiException {
    public ResourceNotFoundException(String resource, Object id) {
        super(HttpStatus.NOT_FOUND, "RESOURCE_NOT_FOUND",
              resource + " not found with id: " + id);
    }
}

public class BusinessException extends ApiException {
    public BusinessException(String errorCode, String message) {
        super(HttpStatus.UNPROCESSABLE_ENTITY, errorCode, message);
    }
}

public class DuplicateResourceException extends ApiException {
    public DuplicateResourceException(String resource, String field, Object value) {
        super(HttpStatus.CONFLICT, "DUPLICATE_RESOURCE",
              resource + " already exists with " + field + ": " + value);
    }
}

🤔 Common Confusions

❓ @ControllerAdvice vs @RestControllerAdvice — difference?

@RestControllerAdvice = @ControllerAdvice + @ResponseBody. Use @RestControllerAdvice for REST APIs — it automatically serializes your return value to JSON. @ControllerAdvice is for MVC apps where you might return view names.

❓ Why does my @ExceptionHandler not catch Spring Security exceptions?

Spring Security exceptions (AccessDeniedException, AuthenticationException) are handled by the security filter chain BEFORE reaching the DispatcherServlet. To handle them, implement AuthenticationEntryPoint (for 401) and AccessDeniedHandler (for 403) and register them in your SecurityFilterChain.

🚨 Common Production Issues

Stack traces returned in error responses — the catch-all handler must not include ex.getMessage() for unexpected exceptions

Validation errors not returning field details — override handleMethodArgumentNotValid in ResponseEntityExceptionHandler

DataIntegrityViolationException from DB constraints returning 500 — catch it and return 409 Conflict

Error responses not in consistent format — establish one ErrorResponse record and use it everywhere

✅ Best Practices

Always have a catch-all @ExceptionHandler(Exception.class) that returns 500 without exposing internals

Log unexpected exceptions with full stack trace; log expected exceptions at WARN level

Use RFC 7807 ProblemDetail format (Spring Boot 3.x) for standards-compliant error responses

Include a request ID in error responses for correlation with logs

Never expose stack traces, class names, or SQL in error responses

🏛️ Architect's Note

Senior Architect's Perspective

Error handling is part of your API contract. Define your error format early and document it in your OpenAPI spec. Clients need to handle errors reliably — if your format changes, you break them. Use RFC 7807 Problem Details (built into Spring Boot 3.x with spring.mvc.problemdetails.enabled=true) for a standards-compliant format that clients can parse without custom code.

💬 Frequently Asked Questions

How do I enable RFC 7807 Problem Details in Spring Boot 3.x?

Set spring.mvc.problemdetails.enabled=true in application.properties. Spring Boot 3.x then uses ProblemDetail as the error response format for all built-in exceptions. For your custom exceptions, return ProblemDetail from your @ExceptionHandler methods.

How do I handle exceptions in async methods (@Async)?

@ControllerAdvice doesn't catch exceptions from @Async methods — they run in a different thread. Use AsyncUncaughtExceptionHandler by implementing AsyncConfigurer.getAsyncUncaughtExceptionHandler(). Or wrap the async logic in a try-catch and handle exceptions there.

🎯 Key Takeaways

1@RestControllerAdvice provides global exception handling for all controllers
2@ExceptionHandler maps exception types to HTTP responses
3Always have a catch-all handler that returns 500 without exposing internals
4Override handleMethodArgumentNotValid for detailed validation error responses
5Spring Security exceptions need AuthenticationEntryPoint and AccessDeniedHandler
6Enable RFC 7807 Problem Details in Spring Boot 3.x for standards compliance

🎯 Practice this topic in interviews

↗ Where this topic fits

Sources

Sources / References

Use these Spring Boot references to verify the concept in official docs and continue into the most relevant SplashRide pages.

Related SplashRide pages

SplashRide Spring Boot learning path

Browse the full Spring Boot topic collection and learning order on SplashRide.

Spring Boot Interview Prep

Practice Spring Boot interview questions and scenario-based answers.

Backend Engineer Career Path

A structured journey through Core Java, Spring Boot, production backend engineering, and interview preparation.

Full Stack Java Developer Career Path

A guided route through frontend foundations, Java backend engineering, delivery workflows, and interview readiness.

Controllers

Master Spring MVC Controllers — the web layer that handles HTTP requests, maps URLs to methods, and returns responses in enterprise REST APIs.

Request Mapping

Master Spring MVC request mapping - how to map HTTP requests to controller methods using @RequestMapping, @GetMapping, @PostMapping, and the full range of mapping options.