A comprehensive error handling library for Kotlin applications, with specific support for Quarkus REST services.
This library consists of two main modules:
- error-core: Core functionality for error handling in any Kotlin application
- error-quarkus: Integration with Quarkus for standardized REST API error handling
- Standardized error representation across your application
- Categorized errors with appropriate HTTP status code mapping
- Support for error location/field information
- Consistent error response format for REST APIs
- Automatic handling of common exceptions
- Support for retryable errors
- Correlation ID inclusion in error responses
Add the JitPack repository to your build file:
repositories {
mavenCentral()
maven { url = uri("https://jitpack.io") }
}Add the dependencies:
// For core functionality only
implementation("com.github.incept5.error-lib:error-core:1.0.0")
// For Quarkus integration
implementation("com.github.incept5.error-lib:error-quarkus:1.0.0")Add the JitPack repository to your pom.xml:
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>Add the dependencies:
<!-- For core functionality only -->
<dependency>
<groupId>com.github.incept5.error-lib</groupId>
<artifactId>error-core</artifactId>
<version>1.0.0</version>
</dependency>
<!-- For Quarkus integration -->
<dependency>
<groupId>com.github.incept5.error-lib</groupId>
<artifactId>error-quarkus</artifactId>
<version>1.0.0</version>
</dependency>The library defines several error categories that map to appropriate HTTP status codes:
enum class ErrorCategory {
AUTHENTICATION, // 401 Unauthorized
AUTHORIZATION, // 403 Forbidden
VALIDATION, // 400 Bad Request
CONFLICT, // 409 Conflict
NOT_FOUND, // 404 Not Found
BAD_GATEWAY, // 502 Bad Gateway
RATE_LIMIT_EXCEEDED, // 429 Too Many Requests (with Retry-After header)
UNEXPECTED, // 500 Internal Server Error
}You can define your own error codes by implementing the ErrorCode interface:
enum class MyErrorCodes : ErrorCode {
INVALID_INPUT,
RESOURCE_NOT_FOUND,
DUPLICATE_ENTRY;
override fun getCode(): String = name
}You can throw a CoreException directly:
throw CoreException(
category = ErrorCategory.VALIDATION,
errors = listOf(Error("INVALID_INPUT", "fieldName")),
message = "Validation failed"
)You can add error metadata to any exception:
val exception = RuntimeException("Something went wrong")
exception.addMetadata(
category = ErrorCategory.VALIDATION,
errors = *arrayOf(Error("INVALID_INPUT", "fieldName")),
retryable = false
)
throw exceptionYou can mark errors as retryable:
throw CoreException(
category = ErrorCategory.UNEXPECTED,
errors = listOf(Error("TEMPORARY_FAILURE")),
message = "Service temporarily unavailable",
retryable = true
)Check if an exception is retryable:
if (exception.isRetryable()) {
// Implement retry logic
}When a caller exceeds a rate limit, throw RateLimitExceededException (or any CoreException
with the RATE_LIMIT_EXCEEDED category) from your throttling site:
throw RateLimitExceededException(
message = "Rate limit exceeded. Maximum 10 requests per minute allowed.",
retryAfterSeconds = 30, // optional, defaults to 60 in the response header
)In Quarkus the handler maps this to an HTTP 429 (Too Many Requests) response with a
Retry-After header set from retryAfterSeconds (or 60 if not supplied). The exception
is always retryable.
Note that the library logs rate limit errors at DEBUG rather than WARN, on the assumption that the throttling site (e.g. an interceptor) already logs each rejection with its own context (bucket key, limit, client) — this avoids double-logging every throttled request.
This library only standardises the 429 response; it does not provide a rate limiting
mechanism. For annotation-driven endpoint throttling see @RateLimit in platform-core-lib.
- Add the
error-quarkusdependency to your project - The library will automatically register exception mappers for common exceptions
The standard error response format is:
{
"errors": [
{
"message": "Error message",
"code": "ERROR_CODE",
"location": "fieldName"
}
],
"correlationId": "unique-correlation-id",
"status": 400
}@Path("/messages")
@Produces(MediaType.APPLICATION_JSON)
class MessageResource(val messageService: MessageService) {
@POST
fun createMessage(@Valid createMessageRequest: CreateMessageRequest): Response {
try {
val response = messageService.createMessage(createMessageRequest)
return Response.created(URI("/messages/${response.id}")).build()
} catch (e: EntityNotFoundException) {
// Option 1: Add metadata to existing exception
e.addMetadata(
ErrorCategory.NOT_FOUND,
Error("RESOURCE_NOT_FOUND", "id")
)
throw e
}
}
@GET
@Path("/{id}")
fun getMessage(@PathParam("id") id: UUID): Response {
val message = messageService.getMessage(id)
?: // Option 2: Throw CoreException directly
throw CoreException(
ErrorCategory.NOT_FOUND,
listOf(Error("MESSAGE_NOT_FOUND", "id")),
"Message not found"
)
return Response.ok(message).build()
}
}Define your own error codes for better organization:
enum class MessageErrorCodes : ErrorCode {
MESSAGE_NOT_FOUND,
INVALID_MESSAGE_FORMAT,
DUPLICATE_MESSAGE;
override fun getCode(): String = name
}
// Usage
throw CoreException(
ErrorCategory.VALIDATION,
listOf(MessageErrorCodes.INVALID_MESSAGE_FORMAT.toError("content")),
"Invalid message format"
)You can extend the RestErrorHandler class to add custom exception handling:
@ApplicationScoped
class CustomErrorHandler : RestErrorHandler() {
@ServerExceptionMapper
fun handleCustomException(
req: HttpServerRequest,
exp: MyCustomException
): Response {
// Custom handling logic
return handleCoreException(
req,
toCoreException(ErrorCategory.VALIDATION, exp, "Custom error")
)
}
}The library automatically handles ConstraintViolationException and maps validation errors to the standard error format.
The error-quarkus-sample module provides a complete example of how to use the library in a Quarkus application.
- Java 21 or higher
- Kotlin 1.9 or higher
- For Quarkus integration: Quarkus 3.x
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.