Exception message not included in response when throwing ResponseStatusException in Spring Boot
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
When you throw a ResponseStatusException with a reason message in Spring Boot 2.3+, the message does not appear in the JSON error response by default. This is because Spring Boot changed the default behavior to hide error details for security reasons — exposing internal error messages to clients can leak implementation details. To include the message, you must set server.error.include-message=always in application.properties, or use a custom error handler.
The Problem
Expected response:
Actual response (Spring Boot 2.3+):
The message field is empty even though you passed "User not found with id: 42" to the exception.
Fix 1: Configure application.properties
Or in YAML:
The options for include-message are:
never(default in Spring Boot 2.3+) — always emptyalways— always includedon_param— included only when the request has amessageparameter
After this change, the reason from ResponseStatusException appears in the response:
Fix 2: Custom Exception Handler with @ControllerAdvice
For full control over the error response format, use @ControllerAdvice:
This bypasses Spring Boot's default error handling entirely and lets you include or exclude any field.
Fix 3: Custom Error Attributes
Override DefaultErrorAttributes to customize what Spring Boot's default error response includes:
Fix 4: Custom Exception Classes
Instead of ResponseStatusException, define specific exception classes for cleaner error handling:
Other Hidden Error Properties
Spring Boot 2.3+ also hides stack traces and binding errors by default:
For production, keep the defaults (never) and use @ControllerAdvice to return only safe, user-facing messages.
Spring Boot Version History
| Version | Default include-message | Behavior |
| Spring Boot 2.0-2.2 | Messages included by default | Reason appears in response |
| Spring Boot 2.3+ | never | Messages hidden by default |
| Spring Boot 3.x | never | Same as 2.3+, uses Jakarta namespace |
The change was introduced in Spring Boot 2.3 (May 2020) as a security improvement to prevent accidental information leakage.
Common Pitfalls
- Not knowing the default changed in Spring Boot 2.3: Code that worked in Spring Boot 2.2 silently stops showing error messages after upgrading. Add
server.error.include-message=alwaysif you relied on the old behavior. - Exposing internal details in production: Setting
include-message=alwaysin production can leak database column names, query details, or internal paths. Use@ControllerAdviceto sanitize messages for production andinclude-message=alwaysonly in development profiles. - Confusing
reasonwithmessage:ResponseStatusExceptionusesgetReason(), notgetMessage(). ThegetMessage()method returns the HTTP status description combined with the reason, whilegetReason()returns just your custom message. - Using
@ResponseStatuson the exception class AND throwingResponseStatusException: If you annotate a custom exception with@ResponseStatusand also wrap it inResponseStatusException, the status codes may conflict. Use one approach consistently. - Forgetting to handle exceptions in reactive WebFlux: In WebFlux,
ResponseStatusExceptionworks differently — it returns the message by default. Theserver.error.include-messageproperty only applies to the servlet (MVC) stack.
Summary
- Spring Boot 2.3+ hides
ResponseStatusExceptionreason messages by default for security - Set
server.error.include-message=alwaysinapplication.propertiesfor the quickest fix - Use
@RestControllerAdvicewith@ExceptionHandlerfor full control over error responses - Define custom exception classes instead of
ResponseStatusExceptionfor cleaner, more maintainable error handling - Keep error messages hidden in production and use structured error responses to avoid leaking internal details
Related reading
- Execute web service method and return immediately
- Executing frozen tensorflow graph that uses tensorflow.contrib.resampler using c_api.h
- Exporting DNS zonefile from Amazon Route 53
- Expose port in minikube
- Exception thrown in catch and finally clause
- Exception when creating datasource with PostgreSQL driver in Spring Boot
- Exception messages in English?
- Exception 'open failed EACCES Permission denied' on Android

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.