# 2. Choosing a mechanism [← 1. Mental model](01-mental-model.md) · [Index](../README.md) · Next: [3. Validation errors →](03-validation-errors.md) The same thirteen failures under five setups - [`matrix-summary.txt`](output/matrix-summary.txt), produced by [`scripts/demo-matrix.sh`](../scripts/demo-matrix.sh): ``` failure | defaults | boot-flag | advice | advice,errors | catchall-first --------------------------------------------------------------------------------------------------------------------------------------------------------------------- domain exception (OrderNotFoundException) | 500 Boot /error JSON | 500 Boot /error JSON | 404 problem+json | 404 problem+json | 500 problem+json type mismatch (/orders/abc) | 400 Boot /error JSON | 400 problem+json | 400 problem+json | 400 problem+json | 500 problem+json exception in a servlet filter | 500 Boot /error JSON | 500 Boot /error JSON | 500 Boot /error JSON | 500 problem+json | 500 Boot /error JSON 401 no credentials | 401 Boot /error JSON | 401 Boot /error JSON | 401 problem+json | 401 problem+json | 401 Boot /error JSON ``` ## `spring.mvc.problemdetails.enabled` Defaults to `false` in Spring Boot 4.1.1 (`spring-configuration-metadata.json` in `spring-boot-webmvc`). When `true`, `WebMvcAutoConfiguration$ProblemDetailsErrorHandlingConfiguration` registers `ProblemDetailsExceptionHandler` - an empty subclass of `ResponseEntityExceptionHandler` - with: - `@ConditionalOnMissingBean(ResponseEntityExceptionHandler.class)` - it backs off as soon as you declare your own subclass ([`advice-order.txt`](output/advice-order.txt), profiles `boot-flag,advice`) - `@Order(0)` - it is consulted before any advice with the default (lowest) precedence It handles exactly what `ResponseEntityExceptionHandler` handles: Spring MVC's exceptions and any `ErrorResponse`. **It does not handle your exceptions**, which is why the first and tenth rows stay Boot's JSON. Turning the flag on and stopping there gives an API with two error shapes. ## Advice ordering, and the catch-all trap `ExceptionHandlerExceptionResolver` walks the advices in order and uses the **first advice that has any matching handler**, not the most specific handler across all advices. The [`catchall-first`](../src/main/java/com/ankurm/problems/advice/CatchAllFirstHandler.java) profile declares `@ExceptionHandler(Exception.class)` at `Ordered.HIGHEST_PRECEDENCE`; it matches `NoResourceFoundException`, `HttpRequestMethodNotSupportedException` and every other framework exception before Boot's handler at order 0 gets a look. Every 4xx in the matrix becomes a 500. Keep the catch-all in the *same* class as your specific handlers (the most specific handler wins within one class), or give it the lowest precedence. ## The handler that stops the application starting Extending `ResponseEntityExceptionHandler` and adding `@ExceptionHandler(MethodArgumentNotValidException.class)` - the obvious way to customise validation errors - fails at startup ([`ambiguous-handler.txt`](output/ambiguous-handler.txt)): ``` Caused by: java.lang.IllegalStateException: Ambiguous @ExceptionHandler method mapped for [ExceptionHandler{exceptionType=org.springframework.web.bind.MethodArgumentNotValidException, mediaType=*/*}]: {org.springframework.http.ProblemDetail com.ankurm.problems.advice.AmbiguousExceptionHandler.invalid(org.springframework.web.bind.MethodArgumentNotValidException), public final org.springframework.http.ResponseEntity org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler.handleException(java.lang.Exception,org.springframework.web.context.request.WebRequest) throws java.lang.Exception} ``` The base class's `handleException` is `final` and already mapped. Override the protected `handleMethodArgumentNotValid(...)` instead - [chapter 3](03-validation-errors.md). ## What the recommended setup is `advice` + `errors`: - [`GlobalExceptionHandler`](../src/main/java/com/ankurm/problems/advice/GlobalExceptionHandler.java) extends `ResponseEntityExceptionHandler`, adds domain handlers and a logging catch-all - [`ProblemDetailErrorController`](../src/main/java/com/ankurm/problems/advice/ProblemDetailErrorController.java) covers the container error page - [chapter 6](06-outside-mvc.md) - [`ProblemDetailSecurityHandlers`](../src/main/java/com/ankurm/problems/security/ProblemDetailSecurityHandlers.java) covers 401/403 with a `WWW-Authenticate` header intact [`AdviceContractTest`](../src/test/java/com/ankurm/problems/AdviceContractTest.java) pins it.