java 124 lines · 3 tabs

Structured Error Responses with @RestControllerAdvice in Spring Boot

Shared by codesnips Jul 2026
3 tabs
package com.example.api.error;

import com.fasterxml.jackson.annotation.JsonInclude;

import java.time.Instant;
import java.util.List;

@JsonInclude(JsonInclude.Include.NON_NULL)
public record ApiError(
        Instant timestamp,
        int status,
        String error,
        String message,
        String path,
        List<FieldError> fieldErrors
) {

    public record FieldError(String field, String message) {
    }

    public static ApiError of(int status, String error, String message, String path) {
        return new ApiError(Instant.now(), status, error, message, path, null);
    }

    public static ApiError of(int status, String error, String message, String path, List<FieldError> fieldErrors) {
        return new ApiError(Instant.now(), status, error, message, path, fieldErrors);
    }
}
3 files · java Explain with highlit

This snippet shows how a Spring Boot API centralizes error handling so that every failure — validation, missing entities, or unexpected exceptions — returns a consistent JSON body instead of the framework's default Whitelabel page. Scattering try/catch blocks across controllers is repetitive and produces inconsistent responses; a single @RestControllerAdvice lets the mapping from exception type to HTTP status and payload live in one place.

The ApiError response tab defines the wire format. It is an immutable value object carrying a timestamp, the numeric status, the reason error, a human message, the request path, and an optional list of fieldErrors. The nested FieldError record holds a field/message pair. Marking the class with @JsonInclude(NON_NULL) keeps the fieldErrors key out of the JSON when there are no validation problems, so a simple 404 stays lean while a 400 can enumerate every bad field.

The GlobalExceptionHandler advice tab is the core. It extends ResponseEntityExceptionHandler so Spring's own handlers (for things like unreadable JSON) can be overridden with the same shape. Each @ExceptionHandler method maps a specific exception to a status: ResourceNotFoundException becomes 404, and MethodArgumentNotValidException becomes 400. The validation handler walks ex.getBindingResult().getFieldErrors() and projects each into an ApiError.FieldError, giving clients machine-readable detail about which inputs failed. A catch-all Exception handler returns 500 and deliberately logs the stack trace while returning a generic message, so internal details never leak to callers. The private build helper stamps the current path from the HttpServletRequest and keeps every response uniform.

The OrderController tab demonstrates the payoff. Its methods stay focused on the happy path: getOrder simply throws ResourceNotFoundException when the service returns nothing, and create relies on @Valid to trigger bean validation. Neither method contains error-formatting code — the advice intercepts thrown exceptions after the controller returns. A key pitfall to remember is scope: @RestControllerAdvice only intercepts exceptions raised within the DispatcherServlet, so errors thrown from filters or during security authentication need separate handling. Ordering also matters, since the most specific @ExceptionHandler wins over the broad Exception fallback.


Related snips

typescript
export interface RetryOptions {
  retries: number;
  baseMs: number;
  maxMs: number;
  signal?: AbortSignal;
  onRetry?: (attempt: number, delay: number, err: unknown) => void;

Exponential backoff with jitter for retries

typescript reliability retry
by codesnips 2 tabs
graphql
type User {
    id: ID!
    name: String!
    email: String!
    posts: [Post!]!
    createdAt: String!

GraphQL API with Spring Boot

java graphql spring-boot
by David Kumar 3 tabs
ruby
class SignupForm
  include ActiveModel::Model
  include ActiveModel::Attributes

  attribute :account_name, :string
  attribute :email, :string

Shallow Controller, Deep Params: Form Object Pattern

rails activemodel form-object
by codesnips 3 tabs
javascript
// Creating a Promise
const myPromise = new Promise((resolve, reject) => {
  const success = true;

  setTimeout(() => {
    if (success) {

Promises and async/await patterns for asynchronous JavaScript

javascript promises async-await
by Alex Chang 1 tab
java
package com.example.starter.config;

import com.example.starter.properties.CustomProperties;
import com.example.starter.service.CustomService;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;

Custom Spring Boot starters

java spring-boot starter
by David Kumar 4 tabs
html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Form Validation Example</title>
  <style>

HTML forms with validation and accessibility

html forms validation
by Alex Chang 1 tab

Share this code

Here's the card — post it anywhere.

Structured Error Responses with @RestControllerAdvice in Spring Boot — share card
Link copied