A lightweight Spring Boot library for converting custom exceptions into standardized RFC 9457 API error responses.
ApiErrorKit provides a simple and extensible way to transform application exceptions into consistent RFC 9457 ProblemDetail responses.
Instead of creating a separate @ExceptionHandler for every custom exception, developers can annotate their exceptions with @ApiError and let ApiErrorKit handle the response mapping automatically.
This project was inspired by a problem I observed during my experience as a Technical Mentor, while coordinating and supporting a team of around 30–40 developers.
When many developers work on the same backend, API error-handling practices can easily become inconsistent. Different developers may return different error formats, status codes, messages, or exception-handling approaches.
Over time, this inconsistency can make debugging more difficult, increase coordination overhead, and make it harder for frontend developers and other team members to work with predictable API contracts.
I built ApiErrorKit to explore how this problem could be addressed through a reusable Spring Boot library and a common API error-handling standard.
Without a common error-handling standard:
- Different developers may return different error response formats.
- The same type of exception may be handled differently across endpoints.
- Frontend developers need to account for inconsistent API error structures.
- Debugging becomes harder because error information is not standardized.
- Changes to error-handling logic may need to be repeated across projects or services.
- Sensitive exception messages may accidentally be exposed to API clients.
ApiErrorKit aims to provide a simple, reusable foundation for consistent API error handling across Spring Boot applications.
The goal is not to replace application-specific exception handling, but to establish a common baseline that teams can extend when necessary.
- Custom exception → RFC 9457
ProblemDetail - Simple
@ApiErrorannotation - Custom HTTP status, title, and type
- Automatic exception resolution
- Inheritance-aware exception resolution
- Validation error handling
- Standard
application/problem+jsonresponses - Secure-by-default exception message handling
- Configurable exception message exposure
- Spring Boot auto-configuration
- Custom
ExceptionResolversupport - Lightweight — no external infrastructure required
Custom Exception
│
▼
@ApiError
│
▼
ExceptionResolver
│
▼
ProblemDetailFactory
│
▼
Spring ProblemDetail
│
▼
application/problem+json
For example:
@ApiError(
status = 404,
title = "User Not Found",
type = "https://api.example.com/problems/user-not-found"
)
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(String message) {
super(message);
}
}When this exception is thrown:
throw new UserNotFoundException("User 123 was not found");ApiErrorKit produces a standardized response:
{
"type": "https://api.example.com/problems/user-not-found",
"title": "User Not Found",
"status": 404,
"detail": "User 123 was not found",
"instance": "/users/123"
}Add the ApiErrorKit starter dependency to your Spring Boot application:
<dependency>
<groupId>io.github.ashan-dissanayake</groupId>
<artifactId>api-error-kit-spring-boot-starter</artifactId>
<version>0.1.1</version>
</dependency>ApiErrorKit
0.1.1is published to Maven Central.
ApiErrorKit does not require custom exceptions to extend a library-specific base exception.
You can use your existing application exceptions.
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(String message) {
super(message);
}
}Annotate the exception with the API error metadata:
@ApiError(
status = 404,
title = "User Not Found",
type = "https://api.example.com/problems/user-not-found"
)
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(String message) {
super(message);
}
}@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
throw new UserNotFoundException(
"User " + id + " was not found"
);
}ApiErrorKit automatically converts the exception into a ProblemDetail response.
ApiErrorKit is secure by default.
Exception messages are not exposed to API clients unless explicitly enabled.
Default:
api-error-kit:
include-exception-message: falseThe response contains:
{
"status": 500,
"title": "Internal Server Error",
"detail": "An unexpected error occurred"
}This helps prevent accidental exposure of sensitive internal information such as:
- database details
- internal service information
- implementation details
- stack-related messages
- infrastructure information
For environments where exposing exception messages is appropriate:
api-error-kit:
include-exception-message: trueThen an annotated exception such as:
throw new UserNotFoundException("User 123 was not found");can produce:
{
"type": "https://api.example.com/problems/user-not-found",
"title": "User Not Found",
"status": 404,
"detail": "User 123 was not found",
"instance": "/users/123"
}ApiErrorKit also handles Spring validation failures.
For example:
public class CreateUserRequest {
@NotBlank
private String name;
@Email
@NotBlank
private String email;
// getters and setters
}Invalid input:
{
"name": "",
"email": "invalid-email"
}Produces:
{
"title": "Validation Failed",
"status": 400,
"detail": "Request validation failed",
"instance": "/users",
"errors": [
{
"field": "name",
"message": "must not be blank"
},
{
"field": "email",
"message": "must be a well-formed email address"
}
]
}ApiErrorKit uses an ExceptionResolver abstraction.
The default implementation is:
AnnotationExceptionResolverApplications can provide their own implementation when custom exception-resolution rules are required.
Example:
@Component
public class CustomExceptionResolver
implements ExceptionResolver {
@Override
public Optional<ResolvedApiError> resolve(
Throwable exception) {
// Custom resolution logic
return Optional.empty();
}
}ApiErrorKit's default resolver is only registered when an application does not provide its own implementation.
Available configuration:
| Property | Default | Description |
|---|---|---|
api-error-kit.include-exception-message |
false |
Controls whether exception messages are included in API error responses |
Example:
api-error-kit:
include-exception-message: falseApiErrorKit is built around Spring's ProblemDetail and the RFC 9457 problem-details format.
Typical response:
{
"type": "https://api.example.com/problems/user-not-found",
"title": "User Not Found",
"status": 404,
"detail": "User 123 was not found",
"instance": "/users/123"
}Validation errors additionally contain an errors extension property.
The project includes unit and integration tests covering:
- Annotated exception resolution
- Unannotated exception fallback
- Inherited
@ApiErrormetadata ProblemDetailcreation- Null exception messages
- Empty exception messages
- Secure exception-message handling
- Validation errors
- HTTP response status and content type
- RFC 9457 response fields
Run the test suite with:
./mvnw testOn Windows:
.\mvnw.cmd testCurrent test suite:
Tests run: 12
Failures: 0
Errors: 0
Skipped: 0
ApiErrorKit 0.1.1 has been published to Maven Central and successfully verified through a separate Spring Boot consumer project.
The consumer project resolves the library directly from Maven Central and verifies auto-configuration, custom exception handling, validation errors, and RFC 9457 responses.
src
├── main
│ ├── java
│ │ └── com.apierrorkit
│ │ ├── annotation
│ │ │ └── ApiError.java
│ │ │
│ │ ├── config
│ │ │ ├── ApiErrorKitAutoConfiguration.java
│ │ │ └── ApiErrorKitProperties.java
│ │ │
│ │ ├── handler
│ │ │ └── ProblemExceptionHandler.java
│ │ │
│ │ ├── problem
│ │ │ └── ProblemDetailFactory.java
│ │ │
│ │ └── resolver
│ │ ├── AnnotationExceptionResolver.java
│ │ ├── ExceptionResolver.java
│ │ └── ResolvedApiError.java
│ │
│ └── resources
│ └── META-INF
│ └── spring
│ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports
│
└── test
└── java
└── com.apierrorkit
├── integration
├── problem
└── resolver
ApiErrorKit intentionally focuses on a small set of responsibilities:
- Standardize API error responses.
- Reduce repetitive exception-handler code.
- Support existing custom exceptions.
- Follow RFC 9457 problem-details conventions.
- Avoid exposing sensitive exception information by default.
- Remain lightweight and easy to integrate.
- Provide extension points without introducing unnecessary complexity.
The project intentionally does not require external infrastructure such as:
- Redis
- Kafka
- Elasticsearch
- Log aggregation platforms
- Authentication providers
- External databases
- Java 17
- Spring Boot 4.x
- Spring MVC
- Spring
ProblemDetail - Jakarta Bean Validation
- Maven
- JUnit
- MockMvc
- GitHub Actions
- Maven Central
Current version: 0.1.1
ApiErrorKit is published to Maven Central and has been verified through an external Spring Boot consumer project.
This project is licensed under the MIT License.