Spring Boot
ErrorController
ResponseEntityExceptionHandler
Exception Handling
Java Development

Using Spring Boot's ErrorController and Spring's ResponseEntityExceptionHandler correctly

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Error handling is one of those things that separates a polished Spring Boot application from a fragile one. Spring provides two primary mechanisms for managing errors: ErrorController for catching low-level servlet errors and ResponseEntityExceptionHandler for handling exceptions thrown within your controller layer. Understanding when to use each, and how to combine them, gives you full control over the error responses your API returns.

What ErrorController Does

ErrorController is a Spring Boot interface that intercepts requests forwarded to the /error path by the embedded servlet container. This happens for errors that occur outside of your controller methods, such as 404 Not Found responses, filter-chain exceptions, or errors thrown before a controller is even invoked.

Think of ErrorController as the safety net at the very bottom of the stack. If nothing else catches an error, the servlet container forwards the request to /error, and your ErrorController implementation handles it.

Implementing ErrorController

java
1@RestController
2public class CustomErrorController implements ErrorController {
3
4    @RequestMapping("/error")
5    public ResponseEntity<Map<String, Object>> handleError(HttpServletRequest request) {
6        Integer statusCode = (Integer) request.getAttribute(
7            RequestDispatcher.ERROR_STATUS_CODE);
8        String message = (String) request.getAttribute(
9            RequestDispatcher.ERROR_MESSAGE);
10
11        Map<String, Object> body = new LinkedHashMap<>();
12        body.put("status", statusCode != null ? statusCode : 500);
13        body.put("error", message != null ? message : "Unexpected error");
14        body.put("timestamp", Instant.now().toString());
15
16        return ResponseEntity.status(statusCode != null ? statusCode : 500).body(body);
17    }
18}

This replaces Spring Boot's default BasicErrorController, giving you a consistent JSON error shape for all servlet-level errors.

What ResponseEntityExceptionHandler Does

ResponseEntityExceptionHandler is a Spring base class designed for use with @ControllerAdvice. It provides default handling for common Spring MVC exceptions like MethodArgumentNotValidException, HttpRequestMethodNotSupportedException, and MissingServletRequestParameterException. You extend it and override methods to customize the response bodies.

This mechanism catches exceptions thrown inside your controllers and any Spring MVC infrastructure exceptions that occur during request processing.

Implementing a ControllerAdvice

java
1@RestControllerAdvice
2public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
3
4    @ExceptionHandler(ResourceNotFoundException.class)
5    public ResponseEntity<Map<String, Object>> handleNotFound(
6            ResourceNotFoundException ex, WebRequest request) {
7        Map<String, Object> body = new LinkedHashMap<>();
8        body.put("status", 404);
9        body.put("error", ex.getMessage());
10        body.put("timestamp", Instant.now().toString());
11        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(body);
12    }
13
14    @Override
15    protected ResponseEntity<Object> handleMethodArgumentNotValid(
16            MethodArgumentNotValidException ex,
17            HttpHeaders headers,
18            HttpStatusCode status,
19            WebRequest request) {
20        Map<String, Object> body = new LinkedHashMap<>();
21        body.put("status", status.value());
22        body.put("errors", ex.getBindingResult().getFieldErrors().stream()
23            .map(fe -> fe.getField() + ": " + fe.getDefaultMessage())
24            .toList());
25        body.put("timestamp", Instant.now().toString());
26        return ResponseEntity.status(status).body(body);
27    }
28}

By extending ResponseEntityExceptionHandler, you inherit sensible defaults for roughly a dozen Spring MVC exceptions and only override the ones you want to customize.

When to Use Each

Use ResponseEntityExceptionHandler (via @ControllerAdvice) for the vast majority of your error handling. It covers application-level exceptions, validation errors, and Spring MVC infrastructure exceptions. This is where you define custom exception classes, format validation error messages, and return domain-specific error payloads.

Use ErrorController for errors that occur outside the controller dispatch lifecycle. The most common example is a 404 when no handler mapping matches the request at all. Filter-chain errors and errors from the servlet container itself also fall into this category. Without a custom ErrorController, these return Spring Boot's default whitelabel error page or its default JSON structure.

Combining Them Correctly

The best practice is to use both together. Your @ControllerAdvice handles all exceptions within the Spring MVC layer, while your ErrorController catches everything else. The key is to make both produce the same error response structure so that API consumers see a consistent format regardless of where the error originated.

java
1// Shared utility for consistent error bodies
2public class ErrorResponseBuilder {
3    public static Map<String, Object> build(int status, String message) {
4        Map<String, Object> body = new LinkedHashMap<>();
5        body.put("status", status);
6        body.put("error", message);
7        body.put("timestamp", Instant.now().toString());
8        return body;
9    }
10}

Both your ErrorController and your @ControllerAdvice can delegate to this shared builder, ensuring every error response follows the same contract.

Common Pitfalls

  • Implementing only ErrorController and neglecting @ControllerAdvice, which causes Spring MVC exceptions to bypass your custom formatting and fall through to the servlet container.
  • Forgetting that @ControllerAdvice does not catch errors from servlet filters or interceptors that run before the dispatcher servlet invokes the controller.
  • Overriding ResponseEntityExceptionHandler methods without calling super, which silently discards the default handling for exceptions you did not intend to customize.
  • Registering multiple @ControllerAdvice classes without specifying @Order, leading to unpredictable exception handler precedence.
  • Returning different error response shapes from ErrorController and @ControllerAdvice, forcing API consumers to handle two distinct error formats.

Summary

  • ErrorController handles servlet-level errors (404s, filter failures) that occur outside the Spring MVC dispatch lifecycle.
  • ResponseEntityExceptionHandler with @ControllerAdvice handles exceptions thrown inside controllers and Spring MVC infrastructure.
  • Use both together to cover all error scenarios in your application.
  • Keep your error response structure consistent across both mechanisms by sharing a common response builder.
  • Always specify @Order when you have multiple @ControllerAdvice classes to control handler precedence.

Course illustration
Course illustration

All Rights Reserved.