Exception Handling with @ControllerAdvice
@ControllerAdvice centralizes error handling across every controller. With @ExceptionHandler methods returning ResponseEntity error bodies — or a quick ResponseStatusException — your API speaks in clean, consistent status codes.
Learn Exception Handling with @ControllerAdvice in our free Java course — a beginner-friendly interactive lesson with worked examples, a practice exercise…
Part of the free Java course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
Before You Start
You should understand Java exceptions and how Spring REST controllers return responses. The validation lesson pairs well with this one.
What You'll Learn
The Big Idea — One Help Desk for the Whole Building
💡 Analogy: Without central handling, every office (controller) keeps its own first-aid kit and gives different answers when something goes wrong. @ControllerAdvice is a single help desk for the whole building: whenever any office raises a problem, it routes to the right specialist ( @ExceptionHandler ) who responds with a consistent, well-formatted answer — the right status code and a tidy error body — every time.
Controllers stay focused on the happy path; the advice handles the unhappy ones.
1️⃣ A Central Exception Handler
Annotate a class with @RestControllerAdvice and add an @ExceptionHandler method per error type. Each returns a ResponseEntity with the right status and a structured body. A catch-all prevents raw stack traces from leaking.
2️⃣ Raising Errors from a Controller
Throw a custom domain exception (handled by the advice) for reusable, meaningful errors, or a ResponseStatusException inline for quick one-offs. Both end up as the correct HTTP status.
3️⃣ How the Right Handler Is Chosen
Spring picks the handler whose exception type best matches what was thrown, falling back to a catch-all. Here is that "match by type" dispatch in plain Java you can run.
🧠 Quick Recall
Answer: the more specific ResourceNotFoundException handler; Exception is the fallback.
Answer: @ResponseBody — return values are serialized as the JSON response body.
🎯 YOUR TURN — Map a 409 Conflict
Add an @ExceptionHandler for a DuplicateEmailException that returns 409 Conflict .
🧩 MINI-CHALLENGE — One handler, two exceptions
Handle both IllegalArgumentException and IllegalStateException with a single handler returning 400.
Common Errors
- ❌ Leaking stack traces. Raw 500s expose internals. Fix: add a catch-all handler with a safe message.
- ❌ Returning 200 for errors. Clients can't detect failure. Fix: set the correct status via ResponseEntity .
- ❌ Plain @ControllerAdvice without @ResponseBody. Bodies aren't JSON. Fix: use @RestControllerAdvice .
- ❌ try/catch in every controller. Duplicated, inconsistent handling. Fix: centralize in the advice.
- ❌ Only a broad Exception handler. All errors look the same. Fix: add specific handlers per type.
- ❌ Wrong status codes. e.g. 500 for bad input. Fix: 400 for client errors, 404 for missing, 409 for conflicts.
Pro Tips
- 💡 Return a consistent error shape (timestamp, status, message, optional fields) across all handlers.
- 💡 Log the full exception server-side but send a sanitized message to clients.
- 💡 Map validation failures ( MethodArgumentNotValidException ) to 400 with per-field messages.
- 💡 Keep a catch-all so nothing escapes as an unformatted 500.
📋 Quick Reference
Element
Purpose
Note
@ControllerAdvice
cross-cutting advice
Spans all controllers
@RestControllerAdvice
advice + @ResponseBody
JSON bodies
@ExceptionHandler
map type to handler
Most specific wins
ResponseEntity
status + body
Explicit control
ResponseStatusException
inline status throw
Quick one-offs
400
Bad Request
Invalid input
404
Not Found
Missing resource
409
Conflict
Duplicate/state clash
500
Internal Server Error
Unexpected failure
📚 Resources / Keep Learning
- 📘 Spring MVC — @ExceptionHandler (docs.spring.io)
- 📗 Spring MVC — REST exception handling (docs.spring.io)
- 📗 HTTP response status codes (MDN)
❓ Frequently Asked Questions
🎉 Lesson Complete!
You can now centralize error handling with @ControllerAdvice , map exception types with @ExceptionHandler , return precise ResponseEntity error bodies, throw ResponseStatusException inline, and pick correct HTTP status codes.
Next up: DTOs & Mapping — why you should not expose entities, and how to convert between them cleanly.
Practice quiz
What does a class annotated with @ControllerAdvice provide?
- Cross-cutting exception handling shared across controllers
- A new controller endpoint
- A database connection
- A scheduled task
Answer: Cross-cutting exception handling shared across controllers. @ControllerAdvice centralizes exception handling (and other advice) across all controllers in one place.
What does @ExceptionHandler(SomeException.class) do?
- Throws the exception
- Maps that exception type to a handler method
- Logs only
- Disables the exception
Answer: Maps that exception type to a handler method. @ExceptionHandler binds a method to handle the listed exception type(s) when thrown by a controller.
What is the benefit of returning ResponseEntity from a handler?
- It hides the body
- It is required by Java
- It caches the response
- You control both the HTTP status and the response body
Answer: You control both the HTTP status and the response body. ResponseEntity lets you set the status code, headers, and body explicitly for the error response.
What does throwing a ResponseStatusException(HttpStatus.NOT_FOUND) result in?
- A 200 response
- A 404 response with that status
- A compile error
- A redirect
Answer: A 404 response with that status. ResponseStatusException maps directly to the given HTTP status, here 404 Not Found.
Which combination makes a @RestController-style advice that returns JSON bodies?
- @Controller only
- @ControllerAdvice + @ResponseBody, or @RestControllerAdvice
- @Service
- @Component only
Answer: @ControllerAdvice + @ResponseBody, or @RestControllerAdvice. @RestControllerAdvice = @ControllerAdvice + @ResponseBody, so handler return values become JSON bodies.
If two @ExceptionHandler methods could match, which is chosen?
- A random one
- The one for the most specific exception type
- The first declared
- Both run
Answer: The one for the most specific exception type. Spring picks the handler whose declared exception type is the most specific match for the thrown exception.
What status is conventional for a failed bean validation on a request body?
- 201 Created
- 403 Forbidden
- 400 Bad Request
- 500 Internal Server Error
Answer: 400 Bad Request. Invalid input is a client error, conventionally returned as 400 Bad Request.
Why centralize error handling in @ControllerAdvice instead of try/catch everywhere?
- It runs faster
- It avoids duplicated handling and gives consistent error responses
- It removes exceptions
- It is mandatory
Answer: It avoids duplicated handling and gives consistent error responses. Centralizing avoids repeating try/catch in every controller and yields uniform, consistent error responses.
What HTTP status best fits an unexpected server-side failure?
- 400
- 404
- 500 Internal Server Error
- 204
Answer: 500 Internal Server Error. Unhandled or unexpected server errors map to 500 Internal Server Error.
Can a single @ExceptionHandler handle multiple exception types?
- No, only one
- Only via inheritance
- Only with reflection
- Yes, by listing several classes in the annotation
Answer: Yes, by listing several classes in the annotation. You can pass an array of exception classes to @ExceptionHandler to handle several types with one method.
Continue this course
- Previous: Bean Validation
- Next: DTOs & Mapping