Error Handling
Arkitekt provides several layers of error handling for use cases and coroutine execution.
Global Error Logger
UseCaseErrorHandler.globalOnErrorLogger is a global callback for logging errors. Set it in your Application.onCreate:
UseCaseErrorHandler.globalOnErrorLogger = { error ->
Logger.logError(error)
}
When is globalOnErrorLogger called?
| Execution mode | Condition | Called? |
|---|---|---|
Async execute |
onError defined |
Yes (error is logged AND passed to onError) |
Async execute |
onError not defined |
No (exception is thrown/unhandled) |
launchWithHandler |
Non-CancellationException |
Yes |
launchWithHandler |
CancellationException with non-cancellation root cause (e.g. via getOrCancel) |
Yes |
launchWithHandler |
Pure CancellationException |
No |
launchWithHandler
launchWithHandler launches a coroutine with built-in try-catch error handling:
fun onButtonClicked() = launchWithHandler {
val data = loadDataUseCase.execute(args).getOrThrow()
// process data
}
Error handling behavior:
- Non-
CancellationException: callsglobalOnErrorLogger, then callsdefaultErrorHandler CancellationExceptioncaused bygetOrCancel: callsglobalOnErrorLoggerwith the original exception- Pure
CancellationException: silently ignored
defaultErrorHandler
defaultErrorHandler is called by launchWithHandler after globalOnErrorLogger for non-cancellation exceptions. By default, it rethrows the exception.
Override it in your ViewModel or Component to handle errors without rethrowing:
override fun defaultErrorHandler(exception: Throwable) {
viewState.error = exception.message
}
getOrCancel
getOrCancel is an extension on kotlin.Result<VALUE> (from cr-usecases). It is the recommended way to abort a coroutine cleanly when a sequential use case call fails:
fun onButtonClicked() = launchWithHandler {
val user = loginUseCase
.execute(LoginData(name, password))
.getOrCancel { error -> showError(error.message) } // side-effect before cancel
// only reached on success
navigateToHome(user)
}
On success, getOrCancel returns the value. On failure, it:
- Calls the optional
doBeforeThrowlambda (skipped if the exception is already aCancellationException) - Throws a
CancellationExceptionwhosecauseis the original exception
The thrown CancellationException bubbles up through launchWithHandler, which logs the original cause via globalOnErrorLogger and then lets the coroutine cancel cleanly — defaultErrorHandler is not called.