
Structured, type-safe error hierarchy for API failures, turning HTTP, network, parsing and validation problems into typed exceptions; includes HTTP-client plugin to extract server payloads and data-validation helpers.
A Kotlin Multiplatform library that provides structured error handling for API calls. It defines a comprehensive hierarchy of error types that can be thrown by API clients, making error handling consistent, type-safe, and easier to maintain across your application.
Use this library when:
ApiCallError (base class)
├─ InvalidDataError
├─ NetworkError // Connection issues, timeouts
└─ HttpError // HTTP status code errors
├─ ServerError // 5xx errors
└─ ClientError // 4xx errors
├─ BadRequest // 400
├─ Unauthorized // 401
├─ Forbidden // 403
├─ NotFound // 404
├─ RateLimitReached // 429
└─ OtherClientError // Other 4xx codes
Add the dependency to your build.gradle.kts:
kotlin {
sourceSets {
commonMain.dependencies {
// For core library only:
implementation("io.github.ahparhizgar:api-call-error:0.1.0")
// For Ktor Client plugin:
implementation("io.github.ahparhizgar:api-call-error-ktor:0.1.0")
}
}
}The Ktor plugin automatically converts HTTP errors, network failures, and serialization issues into typed ApiCallError exceptions.
val client = HttpClient {
install(ApiCallErrorPlugin)
// ...
}If your API returns structured error responses, you can extract them using the extractPayload configuration:
@Serializable
data class ErrorResponse(
val errorKey: String,
val message: String,
val details: Map<String, String>? = null
)
val client = HttpClient {
install(ContentNegotiation) {
json()
}
install(ApiCallErrorPlugin) {
extractPayload { response ->
// Only called for 4xx and 5xx responses
val errorResponse = try {
response.body<ErrorResponse>()
} catch (e: Exception) {
return@extractPayload null
}
ClientErrorExtras(
userMessage = errorResponse.message,
errorKey = errorResponse.errorKey,
payload = errorResponse.details
)
}
}
}suspend fun fetchUser(userId: Int): User {
return try {
client.get("https://api.example.com/users/$userId").body()
} catch (e: NotFound) {
// User doesn't exist
// show proper message or take action
} catch (e: ApiCallError) {
// Extract this when into a function to reuse general error handling logic
when (e) {
is Unauthorized -> {
// Token expired or invalid
refreshTokenAndRetry()
}
is NetworkError -> {
// Network connectivity issue
showOfflineMessage()
}
is InvalidDataError -> {
// Response couldn't be parsed
logParsingError(e)
}
is ServerError -> {
// Server is having issues (5xx)
showServerErrorMessage()
}
is ClientError -> {
// Other 4xx error
showError(e.userMessage ?: "Request failed")
}
}
}
}If you're not using Ktor or want to manually create errors, you can use the error classes directly.
The library provides convenient functions for validating response data:
fun parseUserResponse(userDto: dto): User {
requireData(dto.id != null) {
"Missing required field: id"
}
val age = dto.age?.takeIf { it >= 0 }
?: invalidData("Field 'age' must be non negative")
return User(dto.id, age)
}You can extend the error hierarchy for your specific needs:
class CustomClientError(
override val message: String? = null,
override val cause: Throwable? = null,
override val payload: Any? = null
) : ClientError()A Kotlin Multiplatform library that provides structured error handling for API calls. It defines a comprehensive hierarchy of error types that can be thrown by API clients, making error handling consistent, type-safe, and easier to maintain across your application.
Use this library when:
ApiCallError (base class)
├─ InvalidDataError
├─ NetworkError // Connection issues, timeouts
└─ HttpError // HTTP status code errors
├─ ServerError // 5xx errors
└─ ClientError // 4xx errors
├─ BadRequest // 400
├─ Unauthorized // 401
├─ Forbidden // 403
├─ NotFound // 404
├─ RateLimitReached // 429
└─ OtherClientError // Other 4xx codes
Add the dependency to your build.gradle.kts:
kotlin {
sourceSets {
commonMain.dependencies {
// For core library only:
implementation("io.github.ahparhizgar:api-call-error:0.1.0")
// For Ktor Client plugin:
implementation("io.github.ahparhizgar:api-call-error-ktor:0.1.0")
}
}
}The Ktor plugin automatically converts HTTP errors, network failures, and serialization issues into typed ApiCallError exceptions.
val client = HttpClient {
install(ApiCallErrorPlugin)
// ...
}If your API returns structured error responses, you can extract them using the extractPayload configuration:
@Serializable
data class ErrorResponse(
val errorKey: String,
val message: String,
val details: Map<String, String>? = null
)
val client = HttpClient {
install(ContentNegotiation) {
json()
}
install(ApiCallErrorPlugin) {
extractPayload { response ->
// Only called for 4xx and 5xx responses
val errorResponse = try {
response.body<ErrorResponse>()
} catch (e: Exception) {
return@extractPayload null
}
ClientErrorExtras(
userMessage = errorResponse.message,
errorKey = errorResponse.errorKey,
payload = errorResponse.details
)
}
}
}suspend fun fetchUser(userId: Int): User {
return try {
client.get("https://api.example.com/users/$userId").body()
} catch (e: NotFound) {
// User doesn't exist
// show proper message or take action
} catch (e: ApiCallError) {
// Extract this when into a function to reuse general error handling logic
when (e) {
is Unauthorized -> {
// Token expired or invalid
refreshTokenAndRetry()
}
is NetworkError -> {
// Network connectivity issue
showOfflineMessage()
}
is InvalidDataError -> {
// Response couldn't be parsed
logParsingError(e)
}
is ServerError -> {
// Server is having issues (5xx)
showServerErrorMessage()
}
is ClientError -> {
// Other 4xx error
showError(e.userMessage ?: "Request failed")
}
}
}
}If you're not using Ktor or want to manually create errors, you can use the error classes directly.
The library provides convenient functions for validating response data:
fun parseUserResponse(userDto: dto): User {
requireData(dto.id != null) {
"Missing required field: id"
}
val age = dto.age?.takeIf { it >= 0 }
?: invalidData("Field 'age' must be non negative")
return User(dto.id, age)
}You can extend the error hierarchy for your specific needs:
class CustomClientError(
override val message: String? = null,
override val cause: Throwable? = null,
override val payload: Any? = null
) : ClientError()