skip to content

Exception Handling & Validation

Turning failures into responses: local @ExceptionHandler methods, global advice, RFC 7807 problem details, Bean Validation, and status-carrying exceptions. Consistent error handling is one of the clearest signals of an experienced API developer.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

25

What does @Valid on a Spring MVC controller parameter do, and what happens when validation fails?

level: juniorimportance: must knowfreq 80%

answer

  1. @Valid triggers Hibernate Validator
  2. no BindingResult -> exception -> 400
  3. BindingResult must be right after the arg
  4. RequestBody -> MethodArgumentNotValidException
  5. ModelAttribute -> BindException

basics

~20 s

@Valid tells Spring to run Jakarta Bean Validation constraints (like @NotNull, @Size) on the argument. If a constraint fails and there is no BindingResult next to it, Spring throws an exception that becomes an HTTP 400 Bad Request.

solid answer

~40 s

@Valid (jakarta.validation.Valid) marks a controller argument for validation. When Spring binds a @RequestBody or @ModelAttribute, it invokes the JSR-380 Bean Validation provider (Hibernate Validator by default) to check the field constraints (@NotNull, @Size, @Email, @Min, etc.) on that object. If any constraint is violated and there is no BindingResult/Errors parameter immediately after the validated argument, Spring raises an exception — MethodArgumentNotValidException for @RequestBody, or BindException for @ModelAttribute form binding — which the default handler turns into a 400 Bad Request. If you add a BindingResult right after the @Valid argument, Spring instead injects the errors and lets your method decide what to do, so no exception is thrown.

code

java · 17 lines
java
public record CreateUser(
    @NotBlank String name,
    @Email String email,
    @Min(18) int age) {}

@RestController
@RequestMapping("/users")
class UserController {

    // No BindingResult -> invalid body throws
    // MethodArgumentNotValidException -> 400 by default.
    @PostMapping
    ResponseEntity<Void> create(@Valid @RequestBody CreateUser body) {
        // reached only if body is valid
        return ResponseEntity.status(201).build();
    }
}

go deeper

for a junior

Know that @Valid runs the constraint annotations and a failure yields a 400.

for a middle

Explain the exception types (MethodArgumentNotValidException vs BindException) and the BindingResult opt-out.

for a senior

Discuss handling the exception centrally in @RestControllerAdvice vs in-method BindingResult, and the ordering gotcha.

for a principal

Reason about API error-contract design: consistent error envelopes, when to surface field errors vs generic messages, and provider configuration.

## What @Valid is `@Valid` comes from **Jakarta Bean Validation** (the spec formerly JSR-303/JSR-380), package `jakarta.validation.Valid` (older apps: `javax.validation.Valid`). Bean Validation is a standard for declaring **constraints** on object fields with annotations such as `@NotNull`, `@NotBlank`, `@Size`, `@Min`, `@Max`, `@Email`, `@Pattern`. A **validation provider** actually enforces them; the default in Spring Boot is **Hibernate Validator** (pulled in by `spring-boot-starter-validation`). ## What Spring MVC does with it When a controller method parameter is annotated with `@Valid` (or Spring's `@Validated`), Spring MVC runs the validator **after data binding** and **before your method body executes**: - For a **`@RequestBody`** parameter, the JSON is deserialized by Jackson into the object, then validated. - For a **`@ModelAttribute`** parameter (HTML form / query params), request parameters are bound onto the object, then validated. ## What happens on failure The outcome depends on whether you declare a `BindingResult`/`Errors` parameter **immediately after** the validated argument: 1. **No `BindingResult` present** → Spring throws: - `MethodArgumentNotValidException` for a `@RequestBody` argument, or - `BindException` for a `@ModelAttribute` argument. Both are handled by Spring's default exception resolver and produce **HTTP 400 Bad Request**. 2. **`BindingResult` present** → Spring does **not** throw. It populates the `BindingResult` with the errors and calls your method, so you can inspect `bindingResult.hasErrors()` and respond however you like (e.g., re-render a form). ## Key gotcha: order matters The `BindingResult` must come **directly after** the object it applies to. This is wrong: ```java public String submit(@Valid Order order, Model model, BindingResult result) // BROKEN ``` Here Spring can't associate `result` with `order`, so it throws instead of populating errors, and you may get a confusing exception. ## @Valid vs @Validated `@Valid` is the standard annotation. `@Validated` (`org.springframework.validation.annotation.Validated`) is Spring's variant that additionally supports **validation groups**. Both trigger validation on method arguments; use `@Validated` when you need groups. ## When to use which style - **REST APIs**: prefer letting `MethodArgumentNotValidException` propagate and handle it in a `@RestControllerAdvice` `@ExceptionHandler` to produce a consistent error body. Don't add `BindingResult` unless you truly need custom in-method logic. - **Server-rendered forms**: add `BindingResult` so you can re-display the form with field errors instead of a 400 page.

  • Why does adding a BindingResult parameter change the behavior?
    Its presence tells Spring you intend to handle errors yourself, so instead of throwing MethodArgumentNotValidException/BindException, Spring populates the BindingResult and calls your method. You then check hasErrors().
  • Which dependency provides the validator in Spring Boot?
    spring-boot-starter-validation, which brings in Hibernate Validator (the reference implementation of Jakarta Bean Validation). Without it, constraint annotations are ignored.

saying these in an interview costs you the question

  • Thinking @Valid returns a boolean or that you must call it manually
  • Believing validation still works after removing spring-boot-starter-validation
  • Putting BindingResult anywhere except immediately after the validated argument
  • Assuming @Valid validates path variables or simple @RequestParam values (that needs @Validated on the class + method-level validation)

context

open as a page

What are @ControllerAdvice and @RestControllerAdvice, and how do @ExceptionHandler methods inside them work?

level: juniorimportance: must knowfreq 78%

basics

~20 s

@ControllerAdvice is a class whose @ExceptionHandler methods catch exceptions thrown by many controllers in one place, instead of repeating handlers in each controller. @RestControllerAdvice is the same but adds @ResponseBody, so returned objects become JSON.

open as a page

What is a controller-local @ExceptionHandler method and how do you use one?

level: juniorimportance: must knowfreq 72%

basics

~20 s

A method inside a @Controller annotated with @ExceptionHandler. When a request-handling method in that same controller throws a matching exception, Spring calls the handler instead of letting the error escape, and its return value becomes the response.

open as a page

What is ProblemDetail in Spring MVC, and what RFC / media type does it implement?

level: juniorimportance: must knowfreq 62%

basics

~10 s

ProblemDetail is a Spring class (org.springframework.http.ProblemDetail) that models a standard error body from RFC 7807. It is serialized as application/problem+json with fields like type, title, status, detail, and instance.

open as a page

What is @ResponseStatus and how do you use it to control the HTTP status code returned from a Spring MVC controller?

level: juniorimportance: must knowfreq 70%

basics

~10 s

@ResponseStatus is a Spring annotation that sets the HTTP status code of a response. You put it on a custom exception class or a controller method, e.g. @ResponseStatus(HttpStatus.NOT_FOUND) on a NotFoundException.

open as a page

How do you return an RFC 7807 body from a Spring MVC controller or exception handler?

level: middleimportance: must knowfreq 58%

basics

~10 s

Return a ProblemDetail (or ResponseEntity<ProblemDetail>) from a @RestController method or an @ExceptionHandler in a @ControllerAdvice. You can also throw an ErrorResponseException, which Spring turns into a problem+json body.

open as a page

What is the difference between declarative @ResponseStatus on an exception class and programmatically throwing a ResponseStatusException? When would you choose each?

level: middleimportance: must knowfreq 72%

basics

~20 s

@ResponseStatus is a fixed status baked into an exception class. ResponseStatusException lets you throw an exception with a status (and reason) chosen at runtime, without creating a new class. Use the annotation for stable domain errors, the exception for ad-hoc/dynamic cases.

open as a page

How do you produce a clean JSON error response from MethodArgumentNotValidException for a REST API?

level: seniorimportance: must knowfreq 70%

basics

~10 s

Add a @RestControllerAdvice with an @ExceptionHandler(MethodArgumentNotValidException.class). Read ex.getBindingResult().getFieldErrors(), map each to field + message, and return a 400 with that structured body.

open as a page

How does validation differ between @RequestBody and @ModelAttribute, and which exceptions result?

level: middleimportance: should knowfreq 60%

basics

~10 s

@RequestBody validates a JSON body and throws MethodArgumentNotValidException on failure; @ModelAttribute validates form/query binding and throws BindException. Both become HTTP 400. @ModelAttribute can also carry type-conversion (binding) errors, not just constraint violations.

open as a page

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

level: middleimportance: should knowfreq 55%

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.

open as a page

What method arguments and return types can a local @ExceptionHandler method declare?

level: middleimportance: should knowfreq 48%

basics

~20 s

It can take the exception itself, plus request-related things like WebRequest, HttpServletRequest/Response, HttpSession, Principal, Locale, and HandlerMethod. It returns the same values as a normal controller: ResponseEntity, an @ResponseBody object, a view name, ModelAndView, or void.

open as a page

What are validation groups, and how do you apply different constraints for create vs update using @Validated?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Validation groups let one class hold constraints that apply only in certain scenarios. You tag a constraint with groups = Create.class, then trigger it with Spring's @Validated(Create.class) on the parameter. Plain @Valid can't select groups.

open as a page

How do you scope a @ControllerAdvice to only some controllers using basePackages, assignableTypes, and annotations?

level: seniorimportance: should knowfreq 42%

basics

~10 s

By default a @ControllerAdvice applies to every controller. You can narrow it with its attributes: basePackages/basePackageClasses (controllers in those packages), assignableTypes (controllers of those specific types/interfaces), and annotations (controllers marked with a given annotation).

open as a page

When multiple @ExceptionHandler methods could catch a thrown exception, how does Spring choose which one runs?

level: seniorimportance: should knowfreq 44%

basics

~20 s

Spring picks the handler whose declared exception type is the closest match in the class hierarchy to the thrown exception — the most specific one. A handler for the exact type wins over one for a superclass like RuntimeException.

open as a page

How do you control the HTTP status code and response body from a local @ExceptionHandler? Compare @ResponseStatus and ResponseEntity.

level: seniorimportance: should knowfreq 40%

basics

~10 s

Either annotate the handler with @ResponseStatus(HttpStatus.NOT_FOUND) and return the body object, or return a ResponseEntity that sets status, headers and body directly. ResponseEntity gives per-response control; @ResponseStatus is a fixed static status.

open as a page

How do you customize a ProblemDetail — extension fields, type/instance URIs, and internationalized title/detail?

level: seniorimportance: should knowfreq 33%

basics

~10 s

Use setProperty(name, value) for extra fields, setType/setInstance for URIs, and setTitle/setDetail for text. For i18n, configure a MessageSource and rely on ErrorResponse message codes (problemDetail.<ExceptionClass>) so Spring resolves localized title/detail.

open as a page

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

level: seniorimportance: should knowfreq 40%

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.

open as a page

What exactly does the reason attribute of @ResponseStatus (or the reason of ResponseStatusException) do to the response, and how does MessageSource / Spring Boot's error properties affect it?

level: seniorimportance: should knowfreq 45%

basics

~20 s

With a reason, the ResponseStatusExceptionResolver calls HttpServletResponse.sendError(status, reason) instead of sendError(status). sendError triggers the servlet error page (in Boot, /error and BasicErrorController), which builds the body. Since Spring 5.3 the reason can be resolved as a message code via MessageSource.

open as a page

How does ResponseStatusExceptionResolver fit into the DispatcherServlet's HandlerExceptionResolver chain, and what determines whether @ResponseStatus vs an @ExceptionHandler wins for the same exception?

level: seniorimportance: should knowfreq 40%

basics

~20 s

DispatcherServlet has an ordered list of HandlerExceptionResolvers. ExceptionHandlerExceptionResolver (handles @ExceptionHandler) runs first, then ResponseStatusExceptionResolver (handles @ResponseStatus / ResponseStatusException), then DefaultHandlerExceptionResolver. The first that handles the exception wins, so a matching @ExceptionHandler takes precedence over @ResponseStatus.

open as a page

How do you validate a cross-field rule and when should validation live in the service layer instead of the controller?

level: principalimportance: should knowfreq 35%

basics

~20 s

Cross-field rules use a class-level constraint (a custom annotation + ConstraintValidator on the whole object), surfacing as a global error. Input-shape validation belongs at the controller edge; business rules needing DB/state belong in the service.

open as a page

How does Spring resolve which @ExceptionHandler runs, and how do you control ordering across multiple @ControllerAdvice beans?

level: principalimportance: should knowfreq 40%

basics

~20 s

For a thrown exception, Spring first looks for a matching @ExceptionHandler in the controller itself, then in the @ControllerAdvice beans, choosing the closest exception-type match. When several advices could match, their order (via @Order or Ordered) decides which is consulted first.

open as a page

Besides @ExceptionHandler, what do @InitBinder and @ModelAttribute methods do inside a @ControllerAdvice?

level: middleimportance: nice to knowfreq 32%

basics

~20 s

In a @ControllerAdvice, @InitBinder methods customize how request data is bound (e.g. register a date format or block certain fields) for many controllers, and @ModelAttribute methods add shared values to the model before every handler runs.

open as a page

Explain how Spring resolves and invokes local @ExceptionHandler methods internally, and what such handlers cannot catch.

level: principalimportance: nice to knowfreq 26%

basics

~20 s

DispatcherServlet delegates a thrown exception to its HandlerExceptionResolvers. ExceptionHandlerExceptionResolver finds the controller's @ExceptionHandler methods via ExceptionHandlerMethodResolver and invokes the best match. It can't catch exceptions from Servlet Filters, after the response is committed, or thrown by the handler itself.

open as a page

As a principal, how would you standardize error handling across services using ProblemDetail, and what are the trade-offs vs a custom error envelope?

level: principalimportance: nice to knowfreq 22%

basics

~20 s

Adopt RFC 7807 ProblemDetail as the org-wide error format: stable, documented type URIs; a shared library of ErrorResponse-based exceptions; consistent extension fields (traceId, code). Trade-offs vs a custom envelope: standardization and tooling vs full control and matching a pre-existing wrapper.

open as a page

At scale, how would you design a consistent HTTP error-mapping strategy across a Spring application, and where do @ResponseStatus, ResponseStatusException, and ProblemDetail fit?

level: principalimportance: nice to knowfreq 25%

basics

~20 s

Centralize error mapping in a @ControllerAdvice (often extending ResponseEntityExceptionHandler) so every error returns one consistent body, ideally a ProblemDetail (RFC 7807). Use @ResponseStatus/ResponseStatusException for simple local cases, but don't scatter the contract across many exceptions.

open as a page