🔥 Launch Offer: Interview Preparation Bundle @ ₹2,499 ₹9,999 • 75% OFF. Offer expires in 🔥 Launch Offer: Interview Preparation Bundle @ ₹2,499 ₹9,999 • 75% OFF. Offer expires in Interview Preparation Bundle @ ₹2,499 ₹9,999

00:00:00 Get Bundle
home microservice standardizing-api-error-responses-in-spring-boot-apierror-vs-rfc-7807-problem-details
Interview guide Microservice 15 sections

Standardizing API Error Responses in Spring Boot: ApiError vs RFC 7807 Problem Details

When building REST APIs, most developers focus on returning successful responses. However, one aspect that often gets overlooked is how APIs handle errors. Imagine you're consuming an API…

Start reading
By javainterviewprep June 16, 2026 5 min read

When building REST APIs, most developers focus on returning successful responses. However, one aspect that often gets overlooked is how APIs handle errors.

Imagine you’re consuming an API and receive this response:

{
  "timestamp": "2026-06-15T12:30:45",
  "status": 500,
  "error": "Internal Server Error",
  "trace": "java.lang.NullPointerException..."
}

This response raises several concerns:

  • It exposes internal implementation details.
  • It leaks stack traces, which is a security risk.
  • It is difficult for frontend developers to interpret.
  • Different APIs may return errors in completely different formats.

A well-designed REST API should return consistent, meaningful, and secure error responses. In this article, we’ll explore two popular approaches in Spring Boot:

  1. Custom ApiError response
  2. RFC 7807 Problem Details (ProblemDetail)

Why Standardize Error Responses?

A standardized error format provides several benefits:

  • Consistency across all endpoints.
  • Better user experience for frontend developers.
  • Simpler debugging.
  • Improved security by hiding internal implementation details.
  • Easier integration with web and mobile applications.

Instead of exposing stack traces, a typical error response should look like this:

{
  "timestamp": "2026-06-15T12:30:45",
  "status": 404,
  "error": "Not Found",
  "message": "User not found with id 5",
  "path": "/users/5"
}

This format is clean, predictable, and easy for clients to consume.


Option 1: Creating a Custom ApiError Class

Before Spring Boot 3, many projects implemented their own error response object.

Step 1: Create an ApiError DTO

public class ApiError {

    private LocalDateTime timestamp;
    private int status;
    private String error;
    private String message;
    private String path;

    // Constructors
    // Getters and Setters
}

What Each Field Represents

FieldDescription
timestampTime when the error occurred
statusHTTP status code
errorHTTP status reason
messageDescription of the error
pathRequested API endpoint

Step 2: Create a Global Exception Handler

Spring Boot provides @RestControllerAdvice for centralized exception handling.

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<ApiError> handleUserNotFound(
            UserNotFoundException ex,
            HttpServletRequest request) {

        ApiError error = new ApiError(
                LocalDateTime.now(),
                HttpStatus.NOT_FOUND.value(),
                HttpStatus.NOT_FOUND.getReasonPhrase(),
                ex.getMessage(),
                request.getRequestURI());

        return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
    }
}

Now, whenever a UserNotFoundException is thrown, Spring automatically returns a standardized JSON response.


How the Exception Flow Works

Client Request
      │
      ▼
Controller
      │
      ▼
Service
      │
      ▼
Repository
      │
      ▼
Exception Thrown
      │
      ▼
@RestControllerAdvice
      │
      ▼
ApiError Response
      │
      ▼
Client

This centralized approach eliminates repetitive try-catch blocks throughout your application.


Handling Multiple Exceptions

Different exceptions should return different HTTP status codes.

Example:

@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<ApiError> handleIllegalArgument(
        IllegalArgumentException ex,
        HttpServletRequest request) {

    ApiError error = new ApiError(
            LocalDateTime.now(),
            400,
            "Bad Request",
            ex.getMessage(),
            request.getRequestURI());

    return ResponseEntity.badRequest().body(error);
}

For unexpected exceptions:

@ExceptionHandler(Exception.class)
public ResponseEntity<ApiError> handleException(
        Exception ex,
        HttpServletRequest request) {

    ApiError error = new ApiError(
            LocalDateTime.now(),
            500,
            "Internal Server Error",
            "Something went wrong",
            request.getRequestURI());

    return ResponseEntity.internalServerError().body(error);
}

Notice that we avoid returning the actual exception message for generic exceptions. This prevents leaking internal implementation details.


Returning Validation Errors

Validation errors should provide field-specific feedback.

Consider the request object:

public class UserRequest {

    @NotBlank
    private String name;

    @Email
    private String email;
}

If the client sends:

{
  "name": "",
  "email": "abc"
}

A good error response would be:

{
  "status": 400,
  "message": "Validation Failed",
  "errors": {
    "name": "must not be blank",
    "email": "must be a valid email"
  }
}

This makes it easy for frontend applications to display validation messages next to the corresponding input fields.


Option 2: RFC 7807 Problem Details (Spring Boot 3+)

Spring Framework 6 introduced built-in support for RFC 7807, a standardized format for API error responses.

Instead of creating a custom DTO, you can use the ProblemDetail class.

Example:

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(UserNotFoundException.class)
    public ProblemDetail handleUserNotFound(
            UserNotFoundException ex,
            HttpServletRequest request) {

        ProblemDetail problem =
                ProblemDetail.forStatus(HttpStatus.NOT_FOUND);

        problem.setTitle("User Not Found");
        problem.setDetail(ex.getMessage());
        problem.setInstance(URI.create(request.getRequestURI()));

        return problem;
    }
}

The response becomes:

{
  "type": "about:blank",
  "title": "User Not Found",
  "status": 404,
  "detail": "User not found with id 5",
  "instance": "/users/5"
}

This structure follows the RFC 7807 specification, making your API compatible with a widely accepted industry standard.


Understanding ProblemDetail Fields

FieldPurpose
typeURI identifying the error type
titleShort summary of the error
statusHTTP status code
detailDetailed error description
instanceURI of the request that caused the error

Adding Custom Properties

ProblemDetail is still flexible. You can include custom information:

ProblemDetail problem = ProblemDetail.forStatus(HttpStatus.NOT_FOUND);

problem.setTitle("User Not Found");
problem.setDetail(ex.getMessage());

problem.setProperty("timestamp", LocalDateTime.now());
problem.setProperty("errorCode", "USER_404");

Result:

{
  "type": "about:blank",
  "title": "User Not Found",
  "status": 404,
  "detail": "User not found",
  "timestamp": "2026-06-15T11:30:45",
  "errorCode": "USER_404"
}

This gives you the benefits of both a standard format and application-specific metadata.


ApiError vs ProblemDetail

FeatureApiErrorProblemDetail
Built into Spring❌✅
RFC 7807 compliant❌✅
Fully customizable✅✅
Suitable for legacy projects✅⚠️
Recommended for Spring Boot 3+❌✅

Best Practices

  • Use @RestControllerAdvice for centralized exception handling.
  • Return appropriate HTTP status codes (400, 401, 403, 404, 409, 500).
  • Never expose stack traces to API consumers.
  • Provide meaningful error messages.
  • Return field-level validation errors.
  • Keep the error response format consistent across all endpoints.
  • Use ProblemDetail for new Spring Boot 3+ applications.
  • Continue using ApiError if you’re maintaining an existing legacy codebase.

Conclusion

Error handling is just as important as handling successful responses. A well-designed API should provide clear, consistent, and secure error messages that are easy for clients to understand.

If you’re working on an existing Spring Boot application, a custom ApiError class remains a perfectly valid choice. However, for new Spring Boot 3+ projects, ProblemDetail is the recommended approach since it follows the RFC 7807 standard and is supported out of the box by the Spring Framework.

By implementing standardized error responses, you improve developer experience, enhance API maintainability, and build more robust RESTful services.


Key Takeaways

  • Standardize your API error responses.
  • Use @RestControllerAdvice for centralized exception handling.
  • Avoid exposing internal stack traces.
  • Return meaningful HTTP status codes.
  • Prefer ProblemDetail in Spring Boot 3+.
  • Provide field-specific validation errors.
  • Keep your error format consistent across all endpoints.

A small investment in proper error handling today will save countless hours of debugging and integration effort tomorrow.