CoroutineExceptionHandler is a CoroutineContext element and a last resort for uncaught exceptions from root coroutines launched with launch. Exceptions propagate through the coroutine hierarchy until they meet a supervisor or reach the root; without a handler, the system-wide handler takes over. section 18.3 section 18.6
Attach a handler to a supervised scope
val exceptionHandler = CoroutineExceptionHandler { _, e -> println("[ERROR] ${e.message}") } class ComponentWithScope { private val scope = CoroutineScope( SupervisorJob() + Dispatchers.Default + exceptionHandler ) fun action() = scope.launch { throw UnsupportedOperationException("Ouch!") } } Calling action() produces [ERROR] Ouch!. The handler customizes logging after the root launch fails; it is not a general try-catch mechanism. section 18.3
Apply these rules when placing and using the handler:
- Place the handler on the root coroutine or its scope. A handler on an intermediate launch does not process a child’s exception when the exception continues upward. section 18.3
- Use launch for handler-based uncaught-exception processing. If the topmost coroutine uses async, the consumer must handle the exception through await() on the Deferred. section 18.3.1
- Combine the handler with SupervisorJob when sibling coroutines should continue after one child fails; without supervision, failure can cancel sibling coroutines. section 18.3.1 section 18.6
Listing 18.6 Only the exception handler at the top of the coroutines hierarchy is invoked
import kotlinx.coroutines.*
private val topLevelHandler = CoroutineExceptionHandler { _, e ->
println("[TOP] ${e.message}")
}
private val intermediateHandler = CoroutineExceptionHandler { _, e ->
println("[INTERMEDIATE] ${e.message}")
}
@OptIn(DelicateCoroutinesApi::class) #1
fun main() {
GlobalScope.launch(topLevelHandler) {
launch(intermediateHandler) {
throw UnsupportedOperationException("Ouch!")
}
}
Thread.sleep(1000)
}
// [TOP] Ouch!
Compare root and intermediate handlers in a nested launch hierarchy: only the handler attached to the top-level launch prints [TOP] Ouch!. section 18.3
Listing 18.5 A component with a custom coroutine exception handler
import kotlinx.coroutines.*
import kotlin.time.Duration.Companion.seconds
class ComponentWithScope(dispatcher: CoroutineDispatcher = Dispatchers.Default) {
private val exceptionHandler = CoroutineExceptionHandler { _, e ->
println("[ERROR] ${e.message}")
}
private val scope = CoroutineScope(
SupervisorJob() + dispatcher + exceptionHandler
)
fun action() = scope.launch {
throw UnsupportedOperationException("Ouch!")
}
}
fun main() = runBlocking {
val supervisor = ComponentWithScope()
supervisor.action()
delay(1.seconds)
}
Inspect a complete component that combines SupervisorJob, a dispatcher, and CoroutineExceptionHandler. section 18.3
