Skip to content

Commit f8d8a53

Browse files
committed
Give custom exceptions a @ResponseStatus fallback so unwritable-body errors resolve correctly
When a client sends an un-negotiable Accept (e.g. application/xml) on an error path, the @ExceptionHandler runs and builds its JSON body, but the write fails with HttpMediaTypeNotAcceptableException. Spring does not re-enter @ExceptionHandler resolution for an exception raised inside exception handling, so the original ResourceNotFoundException / InsufficientStockException propagated to the container as a 500. Spring's built-in exceptions implement ErrorResponse and self-resolve body-lessly in this case; our custom RuntimeExceptions had no fallback. Annotating them with @ResponseStatus lets ResponseStatusExceptionResolver answer with the right status (404 / 409) body-lessly. The @ExceptionHandler still wins for normal requests, so the uniform {status,error,message} body — and the recorded 63-case suite (all Accept: */*) — is unchanged. Also drop handleNotAcceptable: it never fired (the write failure isn't re-entrant), and success-path 406 is already produced by Spring's built-in negotiation. Signed-off-by: dhananjay6561 <dhananjayaggarwal6561@gmail.com>
1 parent 2a922d8 commit f8d8a53

3 files changed

Lines changed: 18 additions & 9 deletions

File tree

spring-boot-product-catalog/src/main/java/io/keploy/productcatalog/web/error/GlobalExceptionHandler.java

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import org.springframework.http.ResponseEntity;
77
import org.springframework.http.converter.HttpMessageNotReadableException;
88
import org.springframework.validation.FieldError;
9-
import org.springframework.web.HttpMediaTypeNotAcceptableException;
109
import org.springframework.web.HttpMediaTypeNotSupportedException;
1110
import org.springframework.web.HttpRequestMethodNotSupportedException;
1211
import org.springframework.web.bind.MethodArgumentNotValidException;
@@ -113,14 +112,6 @@ public ResponseEntity<Map<String, Object>> handleNoResource(NoResourceFoundExcep
113112
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(body);
114113
}
115114

116-
@ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
117-
public ResponseEntity<Void> handleNotAcceptable(HttpMediaTypeNotAcceptableException ex) {
118-
// Thrown while writing a response body the client's Accept header can't take (e.g. an error
119-
// hit with Accept: application/xml). This MUST be body-less — the body is exactly what can't
120-
// be negotiated — otherwise it recurses and the catch-all below would mislabel it as a 500.
121-
return ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE).build();
122-
}
123-
124115
@ExceptionHandler(Exception.class)
125116
public ResponseEntity<Map<String, Object>> handleUnexpected(Exception ex) {
126117
// Catch-all backstop: any exception without a more specific handler above still returns the

spring-boot-product-catalog/src/main/java/io/keploy/productcatalog/web/error/InsufficientStockException.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
package io.keploy.productcatalog.web.error;
22

3+
import org.springframework.http.HttpStatus;
4+
import org.springframework.web.bind.annotation.ResponseStatus;
5+
6+
// @ResponseStatus is a fallback: for a normal request the @ExceptionHandler in
7+
// GlobalExceptionHandler wins and returns the uniform {status,error,message} body. But if the
8+
// body can't be written (e.g. the client sent Accept: application/xml), that write failure is not
9+
// re-entrant into @ExceptionHandler resolution, so ResponseStatusExceptionResolver uses this
10+
// status to answer body-lessly with 409 instead of letting the exception surface as a 500.
11+
@ResponseStatus(HttpStatus.CONFLICT)
312
public class InsufficientStockException extends RuntimeException {
413
public InsufficientStockException(String message) {
514
super(message);

spring-boot-product-catalog/src/main/java/io/keploy/productcatalog/web/error/ResourceNotFoundException.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
package io.keploy.productcatalog.web.error;
22

3+
import org.springframework.http.HttpStatus;
4+
import org.springframework.web.bind.annotation.ResponseStatus;
5+
6+
// @ResponseStatus is a fallback: for a normal request the @ExceptionHandler in
7+
// GlobalExceptionHandler wins and returns the uniform {status,error,message} body. But if the
8+
// body can't be written (e.g. the client sent Accept: application/xml), that write failure is not
9+
// re-entrant into @ExceptionHandler resolution, so ResponseStatusExceptionResolver uses this
10+
// status to answer body-lessly with 404 instead of letting the exception surface as a 500.
11+
@ResponseStatus(HttpStatus.NOT_FOUND)
312
public class ResourceNotFoundException extends RuntimeException {
413
public ResourceNotFoundException(String message) {
514
super(message);

0 commit comments

Comments
 (0)