Spring MVC considers a matching @ExceptionHandler on the controller before a matching handler in @RestControllerAdvice.
Spring MVC exception handlers: a controller-local handler wins before global advice
Keep the response contract local when needed
Two controllers throw the same IllegalStateException in the fixture. One controller has a local handler that maps its known receipt conflict to 409. The other has no local handler, so global advice maps the failure to 500. The checked responses show that the shared advice does not overwrite the local contract.
A bare IllegalStateException is intentionally small test input, not a recommended production error taxonomy. Define a specific conflict exception and a stable public response in a real API. ProblemDetail covers the public envelope, while scoped advice limits a global policy to selected controllers.
Test registration, not just annotation text
The standalone fixture registers its advice explicitly. A production web-context test must also check that component scanning installed the intended advice and that authentication filters run before the handler. Do not return internal exception messages to clients by default.
Checked code
@GetMapping("/contract/local-conflict")
String read() { throw new IllegalStateException("conflict"); }
@ExceptionHandler(IllegalStateException.class)
ResponseEntity<String> local(IllegalStateException rejected) {
return ResponseEntity.status(HttpStatus.CONFLICT).body("receipt conflict");
}Verification boundary
The Maven source kit passes LifecycleAndMvcDispatchContractTest.controllerLocalExceptionHandlerWinsBeforeGlobalAdvice on its local Spring Boot 4 and Java 21 fixture.
Cost and ownership
Exception mapping runs only on a failing request. This fixture checks two local outcomes, not every nested cause or ordered-advice interaction. Diagnostic logging, correlation IDs and response schemas need their own tested policy.
Common Mistakes
- Do not assume a global handler always overrides a controller-local one.
- Do not expose raw exception messages as the public API shape.
- Do not treat standalone advice registration as proof of deployed component scanning.
Read next
Spring MVC ProblemDetail: stable errors without leaking internals, Spring MVC validation errors: one public ProblemDetail for two failure paths, Spring @RestControllerAdvice selector: apply an error policy to one controller family, Spring MockMvc standalone tests: know which HTTP layers were assembled.
