skip to content

@ResponseStatus & ResponseStatusException

@ResponseStatus stamps a status onto an exception class, while ResponseStatusException lets you throw one with a status chosen at runtime. Interviewers ask which you would use and why hardcoding a status on a domain exception is questionable.

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

questions

5

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%

answer

  1. Annotation sets HTTP status
  2. On method → success status; on exception → error status
  3. code/value = HttpStatus; optional reason
  4. Static, fixed per type
  5. ResponseStatusExceptionResolver applies it

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.

solid answer

~40 s

@ResponseStatus is a Spring MVC annotation that maps a handler outcome to a specific HTTP status. Two common placements: (1) on a controller method, so the successful response uses that status instead of the default 200, e.g. @ResponseStatus(HttpStatus.CREATED) on a POST handler; (2) on a custom exception class, so whenever that exception propagates out of a handler, Spring's ResponseStatusExceptionResolver catches it and sets the declared status. Its key attribute is code (aliased as value), an HttpStatus enum; you can also pass a reason string. It is declarative and static: the status is fixed per method or exception type and cannot vary on runtime data. When you need a runtime-decided status, you throw a ResponseStatusException instead.

code

java · 21 lines
java
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ProductNotFoundException extends RuntimeException {
    public ProductNotFoundException(String id) {
        super("No product with id " + id);
    }
}

@RestController
class ProductController {
    @GetMapping("/products/{id}")
    public ProductDto get(@PathVariable String id) {
        return repo.find(id)
            .orElseThrow(() -> new ProductNotFoundException(id)); // → 404
    }

    @PostMapping("/products")
    @ResponseStatus(HttpStatus.CREATED) // → 201 on success
    public ProductDto create(@RequestBody NewProduct body) {
        return service.create(body);
    }
}

go deeper

for a junior

Know it sets the status code and can go on an exception or a method.

for a middle

Explain the static nature and that a resolver applies it for exceptions.

for a senior

Contrast with ResponseStatusException and mention the resolver chain.

for a principal

Discuss when annotating exceptions scales vs. becomes an anti-pattern (too many exception classes).

## What it is `@ResponseStatus` is a Spring Web MVC annotation (`org.springframework.web.bind.annotation.ResponseStatus`) that declares which HTTP status code the response should carry. "Declarative" means you state the intent with an annotation instead of writing imperative code that mutates the response. ## Two placements **1. On a controller method.** By default a returning handler produces HTTP 200 (OK). Annotating the method overrides that default for the success path: ```java @PostMapping("/users") @ResponseStatus(HttpStatus.CREATED) // 201 instead of 200 public UserDto create(@RequestBody NewUser body) { ... } ``` This is handled at method-invocation time by `ServletInvocableHandlerMethod`, which reads the annotation and applies the status before writing the body. **2. On an exception class.** When a handler throws an exception, Spring's exception-resolver chain inspects the exception. If the exception type carries `@ResponseStatus`, the `ResponseStatusExceptionResolver` sets that status: ```java @ResponseStatus(HttpStatus.NOT_FOUND) public class ProductNotFoundException extends RuntimeException { } ``` Throw `new ProductNotFoundException()` anywhere in the call stack of a controller method and the client gets 404. ## Attributes - `code` (and its alias `value`) — an `HttpStatus` enum constant (e.g. `HttpStatus.NOT_FOUND`). - `reason` — an optional String; when present it changes how the response is written (via `sendError`, covered in the reason-mapping question). ## Key property: it is static Because the value lives in an annotation, the status is baked in per method or per exception type. You cannot decide "404 vs 409" at runtime from one annotated exception. That limitation is exactly why `ResponseStatusException` (a programmatic alternative) exists. ## Gotchas - On a method, the annotated status wins even if you also try to return a `ResponseEntity` with a different status — avoid mixing them; prefer one mechanism. - If you catch the exception inside the controller and return normally, `@ResponseStatus` never fires — it only triggers when the exception actually propagates out of the handler. - The annotation is read from the raised exception type (and its meta-annotations), not from a wrapped cause. ## When to use Use `@ResponseStatus` on exceptions for a small, fixed set of domain error types where each maps cleanly to one status. Use it on methods to express non-200 success codes like 201/202/204 concisely.

  • Where does @ResponseStatus on an exception actually get processed?
    By the ResponseStatusExceptionResolver, one of the default HandlerExceptionResolver beans in the DispatcherServlet chain. It reads the annotation from the thrown exception type and applies the status.
  • If you both annotate a method with @ResponseStatus(CREATED) and return a ResponseEntity with 200, what happens?
    Mixing is a bug; the method-level @ResponseStatus is applied, but the two mechanisms conflict and behavior is confusing. Pick one — use ResponseEntity when you need to set status dynamically, @ResponseStatus for a fixed success code.

context

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

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

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