skip to content

What is the ErrorResponse / ErrorResponseException contract and how does it relate to ProblemDetail?

level: seniorimportance: should knowfreq 40%

answer

  1. ErrorResponse = status + headers + body(ProblemDetail)
  2. ProblemDetail = body only
  3. ErrorResponseException = throwable ErrorResponse
  4. getDetailMessageCode / getTitleMessageCode → MessageSource i18n
  5. Spring's own web exceptions implement ErrorResponse

basics

~20 s

ErrorResponse is an interface representing a complete error response: HTTP status, headers, and a ProblemDetail body. ErrorResponseException is a ready-made RuntimeException implementing it. Spring's own web exceptions implement ErrorResponse so they render as problem+json automatically.

solid answer

~40 s

org.springframework.web.ErrorResponse is an abstraction over an entire HTTP error response — getStatusCode(), getHeaders(), and getBody() returning a ProblemDetail — decoupling exception classes from how the body is built. ProblemDetail models just the body; ErrorResponse models status + headers + body together. Spring's framework exceptions (ResponseStatusException, MethodArgumentNotValidException, etc.) implement ErrorResponse, which is why they render as RFC 7807 bodies. ErrorResponseException is a concrete RuntimeException implementing ErrorResponse that you can throw directly or subclass for your own exceptions. ErrorResponse also carries message codes — getDetailMessageCode() and getTitleMessageCode() — plus arguments, so a MessageSource can resolve localized detail/title text. Implementing ErrorResponse (vs a plain exception plus an @ExceptionHandler) is the idiomatic way for library/framework exceptions to be self-describing.

code

java · 20 lines
java
import org.springframework.http.HttpStatus;
import org.springframework.http.ProblemDetail;
import org.springframework.web.ErrorResponseException;

// Self-rendering, i18n-ready domain exception
public class OutOfCreditException extends ErrorResponseException {

    public OutOfCreditException(long balance, long cost) {
        super(HttpStatus.PAYMENT_REQUIRED,
              ProblemDetail.forStatusAndDetail(
                  HttpStatus.PAYMENT_REQUIRED,
                  "Your balance " + balance + " is below the cost " + cost),
              null);
        setTitle("Out of credit");
        getBody().setProperty("balance", balance);
    }
    // Detail message code defaults to:
    //   problemDetail.com.example.OutOfCreditException
    // resolved via MessageSource for localization.
}

go deeper

for a junior

Awareness that ErrorResponseException is a throwable that produces a problem body is enough.

for a middle

Distinguish ProblemDetail (body) from ErrorResponse (status+headers+body) and know ErrorResponseException.

for a senior

Explain the message-code i18n mechanism and when to implement ErrorResponse vs write an @ExceptionHandler.

for a principal

Discuss designing a library's exception hierarchy on ErrorResponse for self-describing, localizable, framework-agnostic errors.

## Two layers: body vs whole response - **`ProblemDetail`** (`org.springframework.http.ProblemDetail`) models only the *body* — the RFC 7807 fields. - **`ErrorResponse`** (`org.springframework.web.ErrorResponse`) models the *whole* error response: `getStatusCode()` (an `HttpStatusCode`), `getHeaders()` (an `HttpHeaders`), and `getBody()` (a `ProblemDetail`). It ties status + headers + body into one contract. This separation lets an exception advertise everything Spring needs to render it, without an external `@ExceptionHandler` translating it. ## Why it exists Before Spring 6, mapping an exception to an HTTP response usually meant writing an `@ExceptionHandler`. `ErrorResponse` lets the exception itself be the single source of truth. Any exception that implements `ErrorResponse` is rendered directly by Spring's default exception handling (and by `ResponseEntityExceptionHandler`). Spring converted its own web exceptions — `ResponseStatusException`, `MethodArgumentNotValidException`, `HttpMediaTypeNotSupportedException`, `MissingRequestHeaderException`, and many more — to implement `ErrorResponse`. That is the mechanism behind uniform RFC 7807 output for framework errors. ## ErrorResponseException `org.springframework.web.ErrorResponseException` is a concrete `RuntimeException implements ErrorResponse`. You can: - **Throw it directly**: `throw new ErrorResponseException(HttpStatus.CONFLICT, problemDetail, cause);` - **Subclass it** for a typed domain exception that is self-rendering. ```java public class OutOfCreditException extends ErrorResponseException { public OutOfCreditException(int balance) { super(HttpStatus.PAYMENT_REQUIRED, forStatusAndDetail(HttpStatus.PAYMENT_REQUIRED, "Insufficient credit"), null); getBody().setProperty("balance", balance); } } ``` ## Message resolution / i18n `ErrorResponse` also exposes **message codes** for internationalization: - `getDetailMessageCode()` — defaults to `problemDetail.<fully.qualified.ExceptionClassName>`. - `getTitleMessageCode()` — defaults to `problemDetail.title.<fully.qualified.ExceptionClassName>`. - `getDetailMessageArguments()` — arguments spliced into the localized message. At render time `ResponseEntityExceptionHandler` (or `ErrorResponse.updateAndGetBody(MessageSource, Locale)`) looks these codes up in a configured `MessageSource` and overwrites `detail`/`title` with the localized text. So you can put `problemDetail.com.example.OutOfCreditException=No tienes suficiente crédito` in `messages_es.properties`. The helper `ErrorResponse.builder(...)` also exists for assembling one fluently without a dedicated class. ## Contract vs handler — when to use which - **Implement `ErrorResponse`** (usually by extending `ErrorResponseException`) when the exception is thrown from many places or from library code and should be self-describing and i18n-ready. - **Write an `@ExceptionHandler`** when you are adapting a *third-party* or legacy exception you cannot modify, or when the mapping needs request context. ## Gotchas - `getBody()` returns a live `ProblemDetail`; mutate it in the constructor to add properties. - Message-code lookup only happens if a `MessageSource` is wired and the codes resolve; otherwise the literal `detail`/`title` you set are used. - `ErrorResponse` lives in `spring-web`, so it is shared by MVC and WebFlux.

  • What is the difference between ProblemDetail and ErrorResponse?
    ProblemDetail models only the RFC 7807 response body; ErrorResponse models the whole error response — status code, headers, and the ProblemDetail body — plus message codes for i18n.
  • What message code does ErrorResponse use by default to localize the detail text?
    problemDetail.<fully-qualified exception class name>, looked up in the configured MessageSource; the title uses problemDetail.title.<class name>.
  • Why did Spring make its own exceptions implement ErrorResponse?
    So they are self-describing and render uniformly as RFC 7807 bodies without every app writing @ExceptionHandlers, and so their detail/title can be internationalized via message codes.

saying these in an interview costs you the question

  • Saying ErrorResponse and ProblemDetail are the same thing
  • Claiming ErrorResponseException requires an @ExceptionHandler to render (it renders via the ErrorResponse contract directly)
  • Not knowing framework exceptions implement ErrorResponse and thinking you must map each one manually

context