惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

aimingoo的专栏
aimingoo的专栏
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
小众软件
小众软件
WordPress大学
WordPress大学
宝玉的分享
宝玉的分享
L
LangChain Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
D
Docker
Cyberwarzone
Cyberwarzone
腾讯CDC
V
Vulnerabilities – Threatpost
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
AWS News Blog
AWS News Blog
GbyAI
GbyAI
Stack Overflow Blog
Stack Overflow Blog
MyScale Blog
MyScale Blog
C
CERT Recently Published Vulnerability Notes
T
Threat Research - Cisco Blogs
S
Securelist
C
Cybersecurity and Infrastructure Security Agency CISA
Security Archives - TechRepublic
Security Archives - TechRepublic
Know Your Adversary
Know Your Adversary
Security Latest
Security Latest
N
News and Events Feed by Topic
Attack and Defense Labs
Attack and Defense Labs
V
Visual Studio Blog
博客园 - 司徒正美
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
I
Intezer
P
Privacy International News Feed
爱范儿
爱范儿
T
The Exploit Database - CXSecurity.com
O
OpenAI News
云风的 BLOG
云风的 BLOG
博客园_首页
雷峰网
雷峰网
M
MIT News - Artificial intelligence
Project Zero
Project Zero
I
InfoQ
Hacker News: Ask HN
Hacker News: Ask HN
C
Cyber Attacks, Cyber Crime and Cyber Security
N
News and Events Feed by Topic
S
Security Affairs
S
Secure Thoughts
Y
Y Combinator Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
美团技术团队
The GitHub Blog
The GitHub Blog
B
Blog
H
Hacker News: Front Page

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Reusable Spring Boot 4 Error Handler
Eduardo Issa · 2026-05-08 · via DEV Community

Why use this library

Out of the box, Spring Boot returns a plain 500 Internal Server Error for most
unhandled exceptions, and its default BasicErrorController produces a flat JSON
object that varies between Spring versions and reveals internal details such as
exception class names and stack-trace snippets. This makes it hard for API clients
to handle errors programmatically and exposes information that should stay
server-side.

This library replaces that behaviour with a single, consistent error contract across
every failure mode your API can encounter:

  • Structured, machine-readable bodies. Every error follows RFC 9457 Problem Details with a stable code field (e.g. INVALID_ENUM_VALUE, VALIDATION_FAILED, DUPLICATE_VALUE). Clients can branch on code without parsing human-readable messages.
  • Precise field-level context. JSON deserialization errors include the exact JSONPath ($.address.street), the rejected value, the expected type, and — for enums — the full list of valid values. Validation errors list every constraint violation in one response so clients do not need to submit the form multiple times to discover all problems.
  • No internal leakage. Raw database messages, exception class names, and stack traces never reach the client. Constraint names are extracted from PostgreSQL error messages via regex and presented as opaque codes.
  • Consistent tracing. The Micrometer trace ID is stamped into every error body (traceId) and onto every HTTP response (X-Trace-Id header), so support teams can correlate a client-side error report directly to a distributed trace.
  • Drop-in auto-configuration. Package this module as a JAR and declare it as a dependency — no @Import, no @ComponentScan, no boilerplate. Spring Boot picks up the handlers automatically.

Requirements

Dependency Notes
Spring Boot 4.x Tested on 4.0.6
spring-boot-starter-webmvc Required
spring-boot-starter-validation Required for ValidationExceptionHandler
spring-boot-starter-data-jpa Required for DataExceptionHandler
spring-boot-starter-actuator + spring-boot-micrometer-tracing-brave + micrometer-tracing-bridge-brave Optional — enables traceId in bodies and X-Trace-Id header

Adding the library to a project

Copy the com.example.demo.error package into your project. Spring Boot's component
scan will pick up all @RestControllerAdvice and @Component beans automatically,
provided the package is within your application's scan root.

Packaging as a reusable JAR. If you extract these classes into a shared library,
register them in
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
(one fully-qualified class name per line). Spring Boot reads that file at startup and
activates the listed classes as auto-configurations, so consumers of the JAR need no
@Import or @ComponentScan — the dependency alone is sufficient.

What's included

JsonExceptionHandler — 400 Bad Request

Handles HttpMessageNotReadableException (Jackson deserialization failures). Maps the
Jackson 3 cause chain to a structured response:

Cause code Extra fields
UnrecognizedPropertyException UNKNOWN_JSON_FIELD path, validValues
InvalidFormatException (enum) INVALID_ENUM_VALUE path, invalidValue, expectedType, validValues
InvalidFormatException (scalar) INVALID_FIELD_VALUE path, invalidValue, expectedType
MismatchedInputException TYPE_MISMATCH path, expectedType
InputCoercionException INTEGER_OVERFLOW validRange, line, column
StreamReadException MALFORMED_JSON line, column
Other MALFORMED_REQUEST_BODY
{
  "type": "about:blank",
  "title": "Invalid enum value",
  "status": 400,
  "detail": "Cannot deserialize value 'UNKNOWN' as Category at path $.category — valid values: [ELECTRONICS, BOOKS, CLOTHING]",
  "instance": "/products",
  "code": "INVALID_ENUM_VALUE",
  "path": "$.category",
  "invalidValue": "UNKNOWN",
  "expectedType": "Category",
  "validValues": [
    "ELECTRONICS",
    "BOOKS",
    "CLOTHING"
  ],
  "traceId": "69fcf2db21f488679d633abb34871dbb"
}

Enter fullscreen mode Exit fullscreen mode

ValidationExceptionHandler — 422 Unprocessable Content

Handles MethodArgumentNotValidException (@Valid on @RequestBody) and
HandlerMethodValidationException (@Validated on individual parameters).

{
  "type": "about:blank",
  "title": "Validation failed",
  "status": 422,
  "detail": "One or more fields failed validation",
  "instance": "/products",
  "code": "VALIDATION_FAILED",
  "violations": [
    {
      "path": "$.name",
      "invalidValue": "",
      "message": "must not be blank"
    },
    {
      "path": "$.price",
      "invalidValue": "-1",
      "message": "must be greater than 0"
    }
  ],
  "traceId": "69fcf2db21f488679d633abb34871dbb"
}

Enter fullscreen mode Exit fullscreen mode

DataExceptionHandler — 404 / 409

Handles JPA and JDBC data exceptions. Constraint names are extracted from the PostgreSQL
error message via regex; raw database messages are never forwarded to the client.

Exception Condition code Status
DuplicateKeyException any DUPLICATE_VALUE 409
DataIntegrityViolationException unique constraint DUPLICATE_VALUE 409
DataIntegrityViolationException foreign key constraint REFERENTIAL_INTEGRITY_VIOLATION 409
DataIntegrityViolationException other DATA_INTEGRITY_VIOLATION 409
EmptyResultDataAccessException any RESOURCE_NOT_FOUND 404
EntityNotFoundException any RESOURCE_NOT_FOUND 404

GlobalExceptionHandler — Spring MVC infrastructure + catch-all

Extends ResponseEntityExceptionHandler to handle the full set of Spring MVC exceptions
(405, 415, 400, 404, 408, 413, …) and adds a stable code field to each. A final
@ExceptionHandler(Exception.class) returns 500 INTERNAL_SERVER_ERROR and logs
the stack trace at ERROR level.

ProblemDetailTraceAdvice

ResponseBodyAdvice that appends "traceId": "<hex>" to every ProblemDetail body
produced by an @ExceptionHandler. Scoped to exception handlers only — normal 2xx
responses are not intercepted.

TraceIdResponseHeaderFilter

OncePerRequestFilter that sets an X-Trace-Id: <hex> response header on every
response (success and error alike). Runs at Ordered.LOWEST_PRECEDENCE so it is
guaranteed to execute inside the ServerHttpObservationFilter span context.

Configuration

Jackson — unknown field detection

# Required for JsonExceptionHandler to produce UNKNOWN_JSON_FIELD responses.
# Without this, Jackson silently ignores unrecognised fields and the handler
# never sees an UnrecognizedPropertyException.
spring.jackson.deserialization.fail-on-unknown-properties=true

Enter fullscreen mode Exit fullscreen mode

Tracing

# Fraction of requests that receive a trace ID (0.0–1.0).
# Defaults to 0.1 (10 %). Set to 1.0 in development so every request
# gets an X-Trace-Id header and a traceId field in error bodies.
management.tracing.sampling.probability=1.0
# Disable tracing entirely without removing the dependency.
# ProblemDetailTraceAdvice and TraceIdResponseHeaderFilter become no-ops.
# management.tracing.enabled=false

Enter fullscreen mode Exit fullscreen mode

Logging

Log levels per handler

Each handler logs at a level that reflects the severity of the underlying problem.
Override individual loggers to tune verbosity:

# JsonExceptionHandler — DEBUG for all recognised causes, WARN for unclassified bodies
logging.level.com.example.demo.error.JsonExceptionHandler=DEBUG
# ValidationExceptionHandler — DEBUG for every violation set
logging.level.com.example.demo.error.ValidationExceptionHandler=DEBUG
# DataExceptionHandler — WARN for constraint violations, DEBUG for not-found
logging.level.com.example.demo.error.DataExceptionHandler=DEBUG
# GlobalExceptionHandler — ERROR for unexpected exceptions (stack trace included),
# everything else inherited from Spring's ResponseEntityExceptionHandler (WARN)
logging.level.com.example.demo.error.GlobalExceptionHandler=DEBUG
# Suppress the stack-trace log that Spring MVC itself emits for resolved exceptions.
# Useful in production to avoid double-logging when GlobalExceptionHandler already logs.
logging.level.org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler=ERROR

Enter fullscreen mode Exit fullscreen mode

Stack traces at TRACE level

JsonExceptionHandler and ValidationExceptionHandler deliberately split their log
output into two statements:

  • DEBUG — a one-line summary (field path, violation count, etc.) that is safe to enable in production without flooding logs.
  • TRACE — the full exception stack trace, logged separately via log.trace("Stack trace:", ex).

This means enabling DEBUG gives you actionable context for every bad request without
stack-trace noise. Enable TRACE only when you need to inspect the Jackson or Validator
internals (e.g. to diagnose a misconfigured deserializer):

# Stack traces for JSON deserialization failures
logging.level.com.example.demo.error.JsonExceptionHandler=TRACE
# Stack traces for Bean Validation failures
logging.level.com.example.demo.error.ValidationExceptionHandler=TRACE

Enter fullscreen mode Exit fullscreen mode

GlobalExceptionHandler.handleUnexpected is the exception to this pattern: it logs at
ERROR with the exception object directly (log.error("Unexpected error", ex)), so the
stack trace is always captured for genuinely unexpected failures regardless of the
configured level.

File upload size (for PAYLOAD_TOO_LARGE)

# Maximum size of a single uploaded file (default: 1MB)
spring.servlet.multipart.max-file-size=10MB
# Maximum size of the entire multipart request (default: 10MB)
spring.servlet.multipart.max-request-size=50MB

Enter fullscreen mode Exit fullscreen mode

When either limit is exceeded Spring throws MaxUploadSizeExceededException, which
GlobalExceptionHandler maps to 413 PAYLOAD_TOO_LARGE.

Internationalisation (i18n)

Every title and detail field in the error body is resolved through Spring's MessageSource,
with the locale read from LocaleContextHolder at exception-handling time. Translating error
messages to a new language requires only a properties file — no code changes.

Bundled locales

File Locale
messages.properties English (fallback)
messages_pt_BR.properties Portuguese — Brazil

Adding a locale

Create messages_<language>[_<COUNTRY>].properties alongside the existing files and translate
every key. Spring resolves the closest-matching bundle for the request locale automatically.

# src/main/resources/messages_es.properties
error.validation-failed.title=Validación fallida
error.validation-failed.detail=Uno o más campos no pasaron la validación
error.resource-not-found.title=Recurso no encontrado
error.resource-not-found.detail=El recurso solicitado no fue encontrado.
# … remaining keys …

Enter fullscreen mode Exit fullscreen mode

If you package this library as a JAR and your application defines its own messages.properties,
list both basenames so Spring merges them:

spring.messages.basename=messages,classpath:com/example/demo/error/messages
spring.messages.encoding=UTF-8

Enter fullscreen mode Exit fullscreen mode

Locale resolution

By default, Spring MVC's AcceptHeaderLocaleResolver maps the Accept-Language request header
to a java.util.Locale. When the header is absent or no bundle matches, Spring falls back to the
JVM default locale and then to the root messages.properties.

To resolve locale from a cookie or session instead, declare a LocaleResolver bean:


@Bean
LocaleResolver localeResolver() {
    var resolver = new CookieLocaleResolver("lang");
    resolver.setDefaultLocale(Locale.ENGLISH);
    return resolver;
}

Enter fullscreen mode Exit fullscreen mode

Validation constraint messages

The violations[].message field inside VALIDATION_FAILED responses comes from Bean
Validation
, not from the MessageSource above. To localise constraint messages, add a
ValidationMessages_<locale>.properties file to your classpath:

# src/main/resources/ValidationMessages_pt_BR.properties
jakarta.validation.constraints.NotBlank.message=não deve estar em branco
jakarta.validation.constraints.Size.message=o tamanho deve estar entre {min} e {max}

Enter fullscreen mode Exit fullscreen mode

Bean Validation resolves its bundle against the JVM default locale. To drive it from the
per-request locale, supply a locale-aware MessageInterpolator in your validator configuration.

Full dependency block for tracing support

  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
  </dependency>
  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-micrometer-tracing-brave</artifactId>
  </dependency>
  <dependency>
      <groupId>io.micrometer</groupId>
      <artifactId>micrometer-tracing-bridge-brave</artifactId>
  </dependency>

Enter fullscreen mode Exit fullscreen mode

Without these dependencies the traceId body field and X-Trace-Id header are simply
absent. All other error-handling behaviour is unaffected.

Error code reference

code Status Meaning
MALFORMED_JSON 400 Syntactically invalid JSON
MALFORMED_REQUEST_BODY 400 Body unreadable (unclassified)
UNKNOWN_JSON_FIELD 400 Field not declared on the target type
INVALID_ENUM_VALUE 400 Value not in enum constants
INVALID_FIELD_VALUE 400 Value cannot be coerced to target type
TYPE_MISMATCH 400 Wrong JSON token type for target
INTEGER_OVERFLOW 400 Numeric value outside type range
VALIDATION_FAILED 422 Bean Validation constraint failure
METHOD_VALIDATION_ERROR 422 Method-level validation failure
DUPLICATE_VALUE 409 Unique constraint violated
REFERENTIAL_INTEGRITY_VIOLATION 409 Foreign key constraint violated
DATA_INTEGRITY_VIOLATION 409 Other integrity constraint violated
RESOURCE_NOT_FOUND 404 Entity or query result not found
METHOD_NOT_ALLOWED 405 HTTP method not supported
UNSUPPORTED_MEDIA_TYPE 415 Content-Type not accepted
NOT_ACCEPTABLE 406 Requested Accept type unavailable
MISSING_PATH_VARIABLE 400 Required path variable absent
MISSING_REQUEST_PARAMETER 400 Required query parameter absent
MISSING_REQUEST_PART 400 Required multipart part absent
REQUEST_BINDING_ERROR 400 Servlet request binding failure
ROUTE_NOT_FOUND 404 No handler mapped for the request path
REQUEST_TIMEOUT 503 Async request timed out
PAYLOAD_TOO_LARGE 413 Upload exceeds configured limit
CONVERSION_NOT_SUPPORTED 500 No converter for property type
PARAMETER_TYPE_MISMATCH 400 Query/path parameter type coercion failed
MESSAGE_NOT_WRITABLE 500 Response body could not be serialised
INTERNAL_SERVER_ERROR 500 Unexpected exception

Source code

The source code of the this handlers is available in GitHub:
https://github.com/adzubla/springboot4-error-handler