Skip to content

Simplified Learning Blog

Learning made easy

  • Home
  • Java
    • Core Java Tutorial
    • Java 8
    • What is Rest API in java
    • Spring Framework
    • Type Casting in Java | 2 types Implicit and explicit casting
    • JUnit 5 Tutorial
      • Assertall in JUnit 5
      • Assertions in JUnit 5
  • Java Interview Questions
    • Top 50 Core Java Interview Questions & Answers (2026 Edition)
    • Top 20 Spring Boot Interview Questions for Freshers (2026 Edition): The Ultimate Cheat Sheet
    • Top 40+ Multithreading interview questions
    • Top 10 AWS Lambda interview questions
  • Java Thread Tutorials
    • How to create thread in Java
    • Multithreading in java
    • Daemon Thread in Java | How to create daemon thread in java
    • Java Virtual Threads (Project Loom) in Real Enterprise Applications
    • WebFlux vs. Virtual Threads in 2026: The Senior Architect’s Decision Matrix
  • AWS
    • What is AWS (Amazon Web Services)
    • AWS IAM (Identity and Access Management)
    • AWS SNS | What is SNS
    • What is SQS | AWS SQS (Simple Queue Service)
    • What is AWS Lambda
  • Software Architecture
    • Software Architecture Performance
    • Performance Principles of Software Architecture
    • Practical System Design Examples using Spring Boot, Queues, and Caches (2026 Guide)
    • System Performance Objective
  • Spring Boot Tutorial
    • Spring Boot Rest API Example complete guide
    • Spring MVC vs. Spring Boot in 2026: The Senior Architect’s Definitive Guide
    • Spring Boot Application.properties vs. YAML: The 2026 Architect’s Verdict
  • Core Java Deep Dives
    • Java int to String Conversion: Performance Benchmarks & Memory Pitfalls
    • String to Integer Conversion in Java | Java convert string to int
    • Converting PDF to JSON in Java Top 3 ways to code:
    • Calculate date of birth from age in jquery
    • How to convert excel to PDF using java
    • jcalendar in java swing example
    • Series program in java
  • Tools
    • JSON Formatter & Debugging Guide for Spring Boot Developers
    • Free Character Counter Tool: The Ultimate Guide to Counting Characters, Words, and Text Statistics
  • Tech Blogs
    • Java 21 New Features
    • Is Java Dead? Is java dead, 2023 ?
    • New Features in Java 17
  • Toggle search form

How to Handle Errors in Spring Boot REST APIs (2026 Guide): The Global Exception Handling Pattern

Posted on January 14, 2026January 14, 2026 By Govind No Comments on How to Handle Errors in Spring Boot REST APIs (2026 Guide): The Global Exception Handling Pattern

Spring Boot Global Exception Handling

The “Spaghetti Catch” Anti-Pattern

If I review a Junior Developer’s code and see a try-catch block inside a @RestController, I immediately request changes.

Table of Contents

Toggle
  • Spring Boot Global Exception Handling
    • The “Spaghetti Catch” Anti-Pattern
    • 1. The Architecture: Centralized vs. Distributed Handling
    • 2. The New Standard: RFC 7807 (ProblemDetail)
    • 3. Implementation: The @ControllerAdvice
      • Step 1: The Global Handler Class
      • Step 2: Custom Exceptions (Keep them Simple)
    • 4. Handling Validation Errors (The Tricky Part)
    • 5. Security Exceptions: The Common Pitfall
    • 6. FAQ – Senior Developer Edition
    • Conclusion: The “Zero-Catch” Policy

Why? Because mixing business logic with error handling logic is the fastest way to create unmaintainable “Spaghetti Code.” In 2026, your controllers should be “Happy Paths” only. They should assume everything works perfectly. If something breaks, the infrastructure should catch it, format it, and inform the client.

This guide covers the Global Exception Handling pattern using Spring Boot 3.4+. We will move beyond the legacy ErrorResponse POJOs and adopt the industry-standard RFC 7807 ProblemDetail specification which is now native to Spring Framework 6.

1. The Architecture: Centralized vs. Distributed Handling

To architect a robust API, you must separate Detection from Reporting.

  • Detection: Happens deep in your Service layer (e.g., “User ID 500 not found”).
  • Reporting: Happens at the API layer (e.g., “Return HTTP 404 with JSON”).

Instead of catching the error in the service and returning a null, simply throw a Runtime Exception. A central “Watchtower” (The @ControllerAdvice) will spot that exception flying through the air, catch it, and transform it into a standardized JSON response.

2. The New Standard: RFC 7807 (ProblemDetail)

Before Spring Boot 3, everyone wrote their own ErrorResponse class.

  • Developer A returned: { "error": "Not Found" }
  • Developer B returned: { "message": "Missing ID", "code": 404 }
  • The Frontend Team: Hated us.

In 2026, we use RFC 7807. It is a standardized JSON format for API problems. Spring 6 supports it natively via the ProblemDetail class.

Standard Output:

{
  "type": "https://api.mysite.com/errors/user-not-found",
  "title": "User Not Found",
  "status": 404,
  "detail": "User with ID 123 does not exist in our records.",
  "instance": "/users/123",
  "traceId": "65b93d3d7f0223"
}

3. Implementation: The @ControllerAdvice

Let’s build the “Watchtower.” This class sits globally across your application and intercepts exceptions from any controller.

Step 1: The Global Handler Class

Create a class annotated with @RestControllerAdvice. This is an Aspect-Oriented Programming (AOP) component.

import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import java.net.URI;

@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

    // 1. Handle Specific Custom Exceptions
    @ExceptionHandler(UserNotFoundException.class)
    ProblemDetail handleUserNotFoundException(UserNotFoundException ex) {
        ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND, ex.getMessage());
        problemDetail.setTitle("User Not Found");
        problemDetail.setType(URI.create("https://api.mysite.com/errors/not-found"));
        return problemDetail;
    }

    // 2. Handle Logic/State Exceptions
    @ExceptionHandler(InsufficientFundsException.class)
    ProblemDetail handleInsufficientFunds(InsufficientFundsException ex) {
        ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, ex.getMessage());
        problemDetail.setTitle("Transaction Failed");
        problemDetail.setProperty("currentBalance", ex.getCurrentBalance()); // Adding custom fields!
        return problemDetail;
    }

    // 3. Handle Everything Else (The Safety Net)
    @ExceptionHandler(Exception.class)
    ProblemDetail handleGlobalException(Exception ex) {
        ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.INTERNAL_SERVER_ERROR, "An internal error occurred.");
        problemDetail.setTitle("Internal Server Error");
        return problemDetail;
    }
}

Step 2: Custom Exceptions (Keep them Simple)

Do not bloat your exceptions. They are just signal flares.

public class UserNotFoundException extends RuntimeException {
    public UserNotFoundException(Long id) {
        super("User with ID " + id + " does not exist.");
    }
}

4. Handling Validation Errors (The Tricky Part)

When a user sends bad JSON (e.g., missing email), Spring throws a MethodArgumentNotValidException. We need to override the default behavior to list which fields failed.

Add this method to your GlobalExceptionHandler:

@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(
        MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
    
    ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, "Validation failed for request.");
    problemDetail.setTitle("Validation Error");
    
    // Collect all field errors into a Map
    Map<String, String> errors = new HashMap<>();
    ex.getBindingResult().getFieldErrors().forEach(error -> 
        errors.put(error.getField(), error.getDefaultMessage())
    );
    
    problemDetail.setProperty("fieldErrors", errors);
    
    return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(problemDetail);
}

5. Security Exceptions: The Common Pitfall

Architect’s Note: @ControllerAdvice typically does not catch exceptions thrown by Spring Security filters (like 401 Unauthorized or 403 Forbidden) because those happen before the Controller is even reached.

To handle those globally using the same JSON format, you must configure an AuthenticationEntryPoint.

@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
    
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) 
            throws IOException {
        
        response.setContentType("application/json");
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        
        String json = """
            {
                "type": "about:blank",
                "title": "Unauthorized",
                "status": 401,
                "detail": "%s"
            }
        """.formatted(authException.getMessage());
        
        response.getWriter().write(json);
    }
}

6. FAQ – Senior Developer Edition

Q: Should I return a 200 OK with an error code inside the JSON? A: Never. This is the “GraphQL style” but it breaks REST semantics. If a resource is not found, the HTTP status must be 404. If you return 200 OK for an error, intermediate proxies and caches will cache that error page as a success, causing massive production issues.

Q: How do I include the Trace ID in the error response? A: If you are using Micrometer Tracing (the default in Spring Boot 3), ProblemDetail handles this well. You can extend the ProblemDetail or simply add a property: problemDetail.setProperty("traceId", Tracer.currentSpan().context().traceIdString());. This allows Ops teams to copy the ID from the JSON response and paste it directly into Grafana/Splunk.

Q: Is ProblemDetail mandatory in Spring Boot 3? A: It is not mandatory (you can still use POJOs), but it is enabled by default. If you throw a ResponseStatusException, Spring will render it as a ProblemDetail JSON automatically.

Conclusion: The “Zero-Catch” Policy

In 2026, your goal as an architect is to implement a Zero-Catch Policy in your business logic.

  1. Service throws Exception.
  2. Controller ignores it (lets it bubble up).
  3. GlobalExceptionHandler catches, logs, and formats it.

This results in cleaner code, consistent API responses, and a frontend team that no longer sends you angry Slack messages about parsing random error strings.

Govind

For over 15 years, I have worked as a hands-on Java Architect and Senior Engineer, specializing in building and scaling high-performance, enterprise-level applications. My career has been focused primarily within the FinTech, Telecommunications, or E-commerce sector, where I’ve led teams in designing systems that handle millions of transactions per day.

Checkout my profile here : AUTHOR https://simplifiedlearningblog.com/author/

Related

Spring Boot Tags:Handle Errors in Spring Boot REST APIs

Post navigation

Previous Post: Practical System Design Examples using Spring Boot, Queues, and Caches (2026 Guide)

More Related Articles

Spring MVC vs. Spring Boot in 2026: The Senior Architect’s Definitive Guide Spring Boot
Spring Boot Application.properties vs. YAML: The 2026 Architect’s Verdict Spring Boot

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Recent Posts

  • How to Handle Errors in Spring Boot REST APIs (2026 Guide): The Global Exception Handling Pattern
  • Practical System Design Examples using Spring Boot, Queues, and Caches (2026 Guide)
  • Spring Boot Application.properties vs. YAML: The 2026 Architect’s Verdict
  • Top 20 Spring Boot Interview Questions for Freshers (2026 Edition): The Ultimate Cheat Sheet
  • Spring MVC vs. Spring Boot in 2026: The Senior Architect’s Definitive Guide

Recent Comments

  1. Govind on Performance Principles of Software Architecture
  2. Gajanan Pise on Performance Principles of Software Architecture
Simplified Learning

Demystifying complex enterprise architecture for senior engineers. Practical guides on Java, Spring Boot, and Cloud Native systems.

Explore

  • Home
  • About Us
  • Author Profile: Govind
  • Contact Us

Legal

  • Privacy Policy
  • Terms and Conditions
  • Disclaimer
© 2026 Simplified Learning Blog. All rights reserved.
We use cookies to improve your experience and personalize ads. By continuing, you agree to our Privacy Policy and use of cookies.

Powered by PressBook Green WordPress theme