Interview frequency: …
Short answer
It works as a last safety valve (a global catch) to catch unhandled exceptions in a root coroutine launched through launch to prevent the app from crashing.
Explanation
A CoroutineExceptionHandler is installed at the level of the CoroutineScope or the root job. The handler does not prevent the coroutine itself from being cancelled or the failure from propagating to the parent in a regular job, but it provides a central place to log the error and show a message to the user instead of the app crashing. The handler has no effect on async because it keeps the exception inside the Deferred object to throw it at await().
Code example
val handler = CoroutineExceptionHandler { _, exception ->
Log.e("Coroutines", "Caught unhandled exception: $exception")
}
val scope = CoroutineScope(Dispatchers.Main + Job() + handler)
scope.launch {
throw RuntimeException("Network crash!") // Safely caught by the handler
}Common mistakes
- Trying to install a CoroutineExceptionHandler inside a child coroutine, where the system ignores it and it only responds if it is on the root.
- Expecting the handler to prevent the failed coroutine from stopping the other operations linked to it in a standard job.
Follow-up questions
- What is the essential difference between a regular Job and a SupervisorJob in exception propagation?