skip to content

What does extending ResponseEntityExceptionHandler give you, and how do you customize the responses it produces?

level: middleimportance: should knowfreq 55%

answer

  1. base class for framework exceptions
  2. one @ExceptionHandler over a big list -> protected handleXxx
  3. handleExceptionInternal = the funnel
  4. Spring 6 -> ProblemDetail (RFC 7807)
  5. extend only once

basics

~20 s

ResponseEntityExceptionHandler is a base class with ready-made @ExceptionHandler methods for Spring MVC's standard exceptions (like validation and unreadable-body errors). You extend it in your @ControllerAdvice and override its protected methods to customize the response body or status.

solid answer

~40 s

ResponseEntityExceptionHandler is an abstract base class you extend from a @ControllerAdvice to get consistent handling of Spring MVC's built-in exceptions — MethodArgumentNotValidException, HttpMessageNotReadableException, HttpRequestMethodNotSupportedException, HttpMediaTypeNotSupportedException, MissingServletRequestParameterException, NoHandlerFoundException, and more. Internally it exposes one @ExceptionHandler covering that whole list and dispatches to protected handleXxx methods, each returning ResponseEntity<Object>. To customize, override the specific method (e.g. handleMethodArgumentNotValid to shape validation errors) or override handleExceptionInternal, the single choke point every path funnels through, to standardize the body, headers, and status. In Spring 6 it produces RFC 7807 ProblemDetail bodies by default. You still add your own @ExceptionHandler methods for domain exceptions alongside the inherited ones. Only extend it once per application to avoid conflicting handlers for the same framework exceptions.

code

java · 24 lines
java
@RestControllerAdvice
public class ApiExceptionHandler extends ResponseEntityExceptionHandler {

    // Customize built-in @Valid failures
    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(
            MethodArgumentNotValidException ex, HttpHeaders headers,
            HttpStatusCode status, WebRequest request) {
        var fields = ex.getBindingResult().getFieldErrors().stream()
                .collect(Collectors.toMap(FieldError::getField,
                        fe -> Objects.requireNonNullElse(fe.getDefaultMessage(), "invalid"),
                        (a, b) -> a));
        ProblemDetail body = ProblemDetail.forStatusAndDetail(
                HttpStatus.BAD_REQUEST, "Validation failed");
        body.setProperty("errors", fields);
        return handleExceptionInternal(ex, body, headers, status, request);
    }

    // Domain exception coexists with inherited handlers
    @ExceptionHandler(ResourceNotFoundException.class)
    public ProblemDetail handleNotFound(ResourceNotFoundException ex) {
        return ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND, ex.getMessage());
    }
}

go deeper

for a junior

Know it pre-handles Spring's built-in exceptions so you don't have to.

for a middle

Name key methods (handleMethodArgumentNotValid, handleExceptionInternal) and how to override them.

for a senior

Discuss ProblemDetail in Spring 6, the single-extension rule, and coexisting domain handlers.

for a principal

Decide whether to adopt it vs a hand-rolled envelope, and how it shapes the org-wide API error contract.

## What it is `ResponseEntityExceptionHandler` (package `org.springframework.web.servlet.mvc.method.annotation`) is an **abstract convenience base class** for a `@ControllerAdvice`. It ships pre-written handling for the exceptions Spring MVC itself raises during request processing, so you don't have to write an `@ExceptionHandler` for each one to avoid ugly default error pages / inconsistent JSON. ## Which exceptions it covers A non-exhaustive list it knows about: `MethodArgumentNotValidException` (Bean Validation `@Valid` failures on `@RequestBody`), `HandlerMethodValidationException` (Spring 6.1 method-level validation), `HttpMessageNotReadableException` (malformed/unparseable body), `HttpMessageNotWritableException`, `HttpRequestMethodNotSupportedException` (405), `HttpMediaTypeNotSupportedException` (415), `HttpMediaTypeNotAcceptableException` (406), `MissingServletRequestParameterException`, `MissingPathVariableException`, `ServletRequestBindingException`, `NoHandlerFoundException` (404, if enabled), `NoResourceFoundException`, `AsyncRequestTimeoutException`, `ConversionNotSupportedException`, `TypeMismatchException`, `MaxUploadSizeExceededException`, and `ErrorResponseException`. ## How it dispatches Rather than one `@ExceptionHandler` per exception, the class declares a **single** `@ExceptionHandler({ ...long list... })` method (`handleException`) that switches on the runtime type and calls the corresponding `protected` method — `handleMethodArgumentNotValid(...)`, `handleHttpMessageNotReadable(...)`, `handleHttpRequestMethodNotSupported(...)`, etc. Each of those returns `ResponseEntity<Object>` and ultimately calls the **`handleExceptionInternal(...)`** method — the common funnel where the body, headers, and status are assembled. ## How to customize 1. **Override a specific `handleXxx` method** to reshape one exception's response — the most common is `handleMethodArgumentNotValid` to turn field errors into a clean validation payload. Remember to call the correct signature (in Spring 6 these take `HttpHeaders`, `HttpStatusCode`, and `WebRequest`). 2. **Override `handleExceptionInternal`** to standardize *all* framework-exception responses in one place (e.g. wrap everything in your own error envelope, add a correlation id header, log). 3. **Add your own `@ExceptionHandler`** methods in the same subclass for **domain** exceptions — they coexist with the inherited framework handlers. ## Spring 6 / ProblemDetail As of Spring Framework 6, `ResponseEntityExceptionHandler` returns **`ProblemDetail`** (RFC 7807: `type`, `title`, `status`, `detail`, `instance`) bodies by default, because the handled exceptions now implement `ErrorResponse`. You can enrich them by overriding `updateAndGetBody` / `createResponseEntity` or by customizing the `ProblemDetail`. ## Gotchas - **Extend it at most once.** Two `@ControllerAdvice` beans that both extend it register duplicate `@ExceptionHandler`s for the same framework exceptions → ambiguity. - Getting the **method signature wrong** on an override means you are not actually overriding (silent no-op). Use `@Override` so the compiler catches it — the Spring 6 signatures changed from `HttpStatus` to `HttpStatusCode`. - The class handles framework exceptions; it does **not** magically handle your custom exceptions — you still add those. - `NoHandlerFoundException` (404) is only thrown if `throwExceptionIfNoHandlerFound` is enabled (default true in Boot 3). ## When to use Use it when you want a single, consistent JSON error contract that also covers Spring MVC's own errors (bad JSON, wrong method, validation), not just your domain exceptions. If you only need to translate a couple of domain exceptions, a plain `@RestControllerAdvice` without the base class is simpler.

  • Which single method should you override to standardize the body/status/headers of every framework-exception response?
    handleExceptionInternal — every protected handleXxx method funnels through it, so it is the one choke point to customize all of them (e.g. wrap in a common envelope or log).
  • Why can having two @ControllerAdvice classes both extend ResponseEntityExceptionHandler cause problems?
    Each registers @ExceptionHandler methods for the same set of framework exceptions, producing ambiguous/duplicate handlers. Extend it once; put domain handlers in that one class or in non-extending advices.

saying these in an interview costs you the question

  • Thinking ResponseEntityExceptionHandler automatically handles your custom domain exceptions
  • Writing an override with the old HttpStatus signature so it silently doesn't override in Spring 6
  • Extending it in multiple advice classes
  • Not realizing Spring 6 returns ProblemDetail bodies by default

context