Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Handling HttpMessageNotReadableException in Spring Boot APIs

Tech May 8 3

HttpMessageNotReadableException surfaces whenever Spring fails to translate an incoming request payload into a Java instance, most often via the @RequestBody mechanism. Typical triggers include:

  • Empty Body: A controller method declares @RequestBody but the client omits the payload entirely.
  • Invalid Syntax: The payload contains broken JSON, such as missing quotes around string values or illegal trailing commas.
  • Unsupported Content-Type: The Content-Type header does not align with the payload structure, so no suitable HttpMessageConverter can process the stream.
  • Field Type Mismatch: A JSON value cannot be maped to the target property type—for example, "abc" supplied for an Integer field.
  • GET Requests with Body: Although technical permissible, GET requests rarely carry a body; applying @RequestBody to a GET handler usually results in this exception when the body is absent.

A global @RestControllerAdvice centralizes recovery:

import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.HandlerMethod;

@RestControllerAdvice
public class RequestExceptionHandler {

    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ErrorResponse handleParseFailure(HttpMessageNotReadableException ex, HandlerMethod handler) {
        Throwable rootCause = ex.getRootCause();

        if (rootCause instanceof InvalidFormatException invalidEx) {
            String hint = TypeHintResolver.generateHint(invalidEx);
            String detail = String.format("Payload parse failure: %s", ex.getMessage());
            return ErrorResponse.failure(hint, "400", detail);
        }

        String clientMsg = "Request body could not be read. Please verify the data format.";
        String logMsg = String.format("HttpMessageNotReadableException: %s", ex.getMessage());
        return ErrorResponse.failure(clientMsg, "400", logMsg);
    }
}

User-friendly hints for type errors improve API usability:

import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import java.math.BigDecimal;
import java.math.BigInteger;

public class TypeHintResolver {

    public static String generateHint(InvalidFormatException ex) {
        Class<?> expected = ex.getTargetType();
        String constraint = mapConstraint(expected);
        Object received = ex.getValue();
        return String.format("Invalid field format. %sInput received: [%s]", constraint, received);
    }

    private static String mapConstraint(Class<?> type) {
        if (isIntegerFamily(type)) {
            return "An integer value is required; ";
        }
        if (isFloatingPointFamily(type)) {
            return "A numeric decimal is required; ";
        }
        return "";
    }

    private static boolean isIntegerFamily(Class<?> clazz) {
        return clazz == Integer.class || clazz == Long.class || clazz == Short.class 
            || clazz == Byte.class || clazz == BigInteger.class
            || clazz == int.class || clazz == long.class || clazz == short.class || clazz == byte.class;
    }

    private static boolean isFloatingPointFamily(Class<?> clazz) {
        return clazz == BigDecimal.class || clazz == Double.class || clazz == Float.class
            || clazz == double.class || clazz == float.class;
    }
}

The following endpoint accepts a member registration DTO:

import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/members")
public class MemberController {

    @PostMapping
    public ResponseEntity<Void> register(@Valid @RequestBody MemberCreateDto request) {
        return ResponseEntity.accepted().build();
    }
}

The DTO defines the expected shape and types:

import jakarta.validation.constraints.*;
import java.time.LocalDateTime;
import lombok.Data;

@Data
public class MemberCreateDto {

    @NotBlank
    private String fullName;

    @Pattern(regexp = "^1[3-9]\\d{9}$")
    private String contactPhone;

    @Email
    private String emailAddress;

    private LocalDateTime activeFrom;
    private LocalDateTime activeUntil;

    @Min(18)
    private Integer age;

    @Max(100)
    private Double rating;
}

Sending an empty body, passing "eighty" for age, or submitting malformed JSON such as an unquoted string value all trigger HttpMessageNotReadableException. The handler returns 400 Bad Request with a clear client message, ensuring that parse failures never expose internal stack traces while still preserving diagnostic details for logging.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.