Reusable Spring Boot Error Handler — DeepSeek Tips &…
    Neura MarketNeura Market/DeepSeek
    ChatGPTChatGPTClaudeClaudeGeminiGeminiCursorCursorGrokGrokPerplexityPerplexityDeepSeekDeepSeek
    CoPilotCoPilotStable DiffusionStable DiffusionMidjourneyMidjourney
    View All Directories
    OverviewRulesPromptsMCPsAgentsGamesBlogVideosGuidesCoursesCommunityTrending
    DeepSeekBlogReusable Spring Boot Error Handler
    Back to Blog
    Reusable Spring Boot Error Handler
    springboot

    Reusable Spring Boot Error Handler

    Eduardo Issao Ito May 8, 2026
    0 views

    Reusable Spring Boot auto-configuration that turns exceptions into structured RFC 9457 Problem Details responses and propagates the Micrometer trace ID to every response.


    title: Reusable Spring Boot Error Handler published: true description: Reusable Spring Boot auto-configuration that turns exceptions into structured RFC 9457 Problem Details responses and propagates the Micrometer trace ID to every response. tags: springboot, errorhandler, problemdetails, rfc9457

    cover_image: https://direct_url_to_image.jpg

    Use a ratio of 100:42 for best results.

    published_at: 2026-05-08 13:11 +0000


    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

    DependencyNotes
    Spring Boot 4.xTested on 4.0.6
    spring-boot-starter-webmvcRequired
    spring-boot-starter-validationRequired for ValidationExceptionHandler
    spring-boot-starter-data-jpaRequired for DataExceptionHandler
    spring-boot-starter-actuator + spring-boot-micrometer-tracing-brave + micrometer-tracing-bridge-braveOptional — 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:

    CausecodeExtra fields
    UnrecognizedPropertyExceptionUNKNOWN_JSON_FIELDpath, validValues
    InvalidFormatException (enum)INVALID_ENUM_VALUEpath, invalidValue, expectedType, validValues
    InvalidFormatException (scalar)INVALID_FIELD_VALUEpath, invalidValue, expectedType
    MismatchedInputExceptionTYPE_MISMATCHpath, expectedType
    InputCoercionExceptionINTEGER_OVERFLOWvalidRange, line, column
    StreamReadExceptionMALFORMED_JSONline, column
    OtherMALFORMED_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"
    }
    

    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"
    }
    

    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.

    ExceptionConditioncodeStatus
    DuplicateKeyExceptionanyDUPLICATE_VALUE409
    DataIntegrityViolationExceptionunique constraintDUPLICATE_VALUE409
    DataIntegrityViolationExceptionforeign key constraintREFERENTIAL_INTEGRITY_VIOLATION409
    DataIntegrityViolationExceptionotherDATA_INTEGRITY_VIOLATION409
    EmptyResultDataAccessExceptionanyRESOURCE_NOT_FOUND404
    EntityNotFoundExceptionanyRESOURCE_NOT_FOUND404

    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
    

    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
    

    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
    

    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
    

    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
    

    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

    FileLocale
    messages.propertiesEnglish (fallback)
    messages_pt_BR.propertiesPortuguese — 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 …
    

    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
    

    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;
    }
    

    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}
    

    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>
    

    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

    codeStatusMeaning
    MALFORMED_JSON400Syntactically invalid JSON
    MALFORMED_REQUEST_BODY400Body unreadable (unclassified)
    UNKNOWN_JSON_FIELD400Field not declared on the target type
    INVALID_ENUM_VALUE400Value not in enum constants
    INVALID_FIELD_VALUE400Value cannot be coerced to target type
    TYPE_MISMATCH400Wrong JSON token type for target
    INTEGER_OVERFLOW400Numeric value outside type range
    VALIDATION_FAILED422Bean Validation constraint failure
    METHOD_VALIDATION_ERROR422Method-level validation failure
    DUPLICATE_VALUE409Unique constraint violated
    REFERENTIAL_INTEGRITY_VIOLATION409Foreign key constraint violated
    DATA_INTEGRITY_VIOLATION409Other integrity constraint violated
    RESOURCE_NOT_FOUND404Entity or query result not found
    METHOD_NOT_ALLOWED405HTTP method not supported
    UNSUPPORTED_MEDIA_TYPE415Content-Type not accepted
    NOT_ACCEPTABLE406Requested Accept type unavailable
    MISSING_PATH_VARIABLE400Required path variable absent
    MISSING_REQUEST_PARAMETER400Required query parameter absent
    MISSING_REQUEST_PART400Required multipart part absent
    REQUEST_BINDING_ERROR400Servlet request binding failure
    ROUTE_NOT_FOUND404No handler mapped for the request path
    REQUEST_TIMEOUT503Async request timed out
    PAYLOAD_TOO_LARGE413Upload exceeds configured limit
    CONVERSION_NOT_SUPPORTED500No converter for property type
    PARAMETER_TYPE_MISMATCH400Query/path parameter type coercion failed
    MESSAGE_NOT_WRITABLE500Response body could not be serialised
    INTERNAL_SERVER_ERROR500Unexpected exception

    Source code

    The source code of the these handlers is available in GitHub.

    • Spring Boot 4: https://github.com/adzubla/springboot4-error-handler
    • Spring Boot 3: https://github.com/adzubla/springboot3-error-handler

    Tags

    springbooterrorhandlerproblemdetailsrfc9457

    Comments

    More Blog

    View all
    Five Gemma-4 models, one accelerator: what porting E2B 31B to AWS Inferentia2 taught megemma

    Five Gemma-4 models, one accelerator: what porting E2B 31B to AWS Inferentia2 taught me

    I ported the whole Gemma-4 family — E2B, E4B, 12B, 31B, and the 26B-A4B MoE — to run on...

    X
    xbill
    Hey DEV, I'm Tobore. Let's actually connect.community

    Hey DEV, I'm Tobore. Let's actually connect.

    Hey DEV, I'm Tobore. Let's actually connect. I've been on here for a while now, mostly writing and...

    L
    Laurina Ayarah
    I burned through thousands of AI tokens. Then a friend did it for freeai

    I burned through thousands of AI tokens. Then a friend did it for free

    (yep, kinda clickbait, just for the funsies 😊) At the beginning of the year, I relaunched my...

    P
    Paulo Henrique
    Claude might be saturating your machineai

    Claude might be saturating your machine

    My laptop was sitting idle with the fan at full tilt. Nothing was running that I knew of. The culprit...

    S
    Sidhant Panda
    Automated GitHub Code Reviews Using Google Geminigithubactions

    Automated GitHub Code Reviews Using Google Gemini

    I Built a Thing! TL;DR — Google Gemini-based Pull Request reviews and Issue Triaging for...

    D
    Darren "Dazbo" Lester
    What is an "agentic harness," actually?ai

    What is an "agentic harness," actually?

    I've been hearing the word "harness" thrown around a lot lately. I assumed it just meant "the IDE" or...

    T
    Tilde A. Thurium

    Stay up to date

    Get the latest DeepSeek prompts, rules, and resources delivered to your inbox weekly.

    Neura Market LogoNeura Market

    Discover the best AI prompts, plugins, and resources for DeepSeek and more.

    Content Types

    • Rules
    • Prompts
    • MCPs
    • Agents
    • Guides

    Platforms

    • ChatGPT Directory
    • Claude Directory
    • Gemini Directory
    • Cursor Directory
    • Grok Directory
    • Perplexity Directory
    • DeepSeek Directory
    • CoPilot Directory
    • Stable Diffusion Directory
    • Midjourney Directory
    • All Directories

    Resources

    • Blog
    • Documentation
    • Help Center
    • Marketplace

    Legal

    • Privacy Policy
    • Terms of Service

    © 2026 Neura Market. All rights reserved.

    |

    Not affiliated with any AI platform vendors.

    Neura Market

    Custom AI Systems & Services

    Our team of experienced AI builders will help build custom AI systems, workflows, and solutions for your business.

    Request custom work

    Ready-made automations for this

    Workflows from the Neura Market marketplace related to this DeepSeek resource

    • Transform Unstructured Data into Structured IdeaBlocks with Blockifyn8n · $14.99 · Related topic
    • Generate Structured Company Descriptions with Bedrijfsdata Web GPT & OpenAIn8n · $14.99 · Related topic
    • Daily Insight Email from Structured Web Data with Firecrawln8n · $14.99 · Related topic
    • Send Structured Logs to BetterStack from Any Workflow Using HTTP Requestn8n · $4.99 · Related topic
    Browse all workflows