Instructions

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

Coroutines are asynchronous. That means that they may execute in a different context than the caller. So coroutine builders like launch or async return immediately and don’t propagate exceptions to the caller. Their invocations don’t throw exceptions, so wrapping them in try-catch blocks doesn’t make sense.

On the other hand, the runBlocking builder is synchronous from the caller’s perspective. It blocks the calling thread until the coroutine finishes and optionally throws the exceptions thrown by the coroutine. So, to handle exceptions thrown by the coroutine inside the runBlocking block, you can wrap it in a try-catch block.

You can use the try-catch blocks, or other Kotlin constructs like runCatching normally in coroutines or suspending functions to recover from the exceptions thrown by the code inside. Starting nested coroutines using the launch or async builders doesn’t change the rules of exception handling. Their invocations don’t throw exceptions, no matter if they are inside the coroutine or not.

There’s one important thing to remember. Coroutine cancelation under the hood is based on throwing a CancellationException. So, you should either not catch CancellationExceptions or, if you’ve already caught them, you have to rethrow them. If the CancellationException is caught and not rethrown, the coroutine cancellation won’t work. The coroutine will continue to run.

What happens with the uncaught exceptions thrown inside the coroutines which aren’t in the runBlocking block?

It depends on several factors. By default, they’re propagated to the parent coroutine scope, which in turn, propagates them to their parent scope and so on until the exceptions reach the root scope. Also, by default, if the parent coroutine receives an exception from its child, it cancels all its children and then itself.

The root scope is either the very first scope in the hierarchy or the scope having the SupervisorJob as its job. You can create a first scope by using the CoroutineScope or MainScope factory functions. Or you can rely on Android-based scopes, such as the viewModelScope.

Coroutine Exception Handlers

The root scope can have the CoroutineExceptionHandler added to its CoroutineContext. It can look like this:

CoroutineScope(
  SupervisorJob() + CoroutineExceptionHandler { _, throwable ->
  Log.e("Lesson5", "Exception handler", throwable)
 }
)
val scope = CoroutineScope(CoroutineExceptionHandler { _, throwable ->
  Log.e("Lesson5", "Exception handler", throwable)
})

scope.async { throw RuntimeException("Test") }

Supervision

If you want to prevent the parent coroutine from being canceled when its child throws an exception, you can use a SupervisorJob. All uncaught exceptions from the children will reach the scope if it has a SupervisorJob in its context. Such uncaught exceptions won’t be propagated to the parent scope. The SupervisorJob is a root scope.

coroutineScope & supervisorScope

The coroutineScope is a suspending function that creates a new coroutine scope and suspends until all the children coroutines finish. Its invocation also throws all the uncaught exceptions from its lambda and all of its child coroutines.

val scope = CoroutineScope(CoroutineExceptionHandler { _, throwable ->
  Log.e("Lesson5", "Exception handler", throwable) // 1 
})

scope.launch {
  try {
    coroutineScope {
      throw RuntimeException("coroutineScope went wrong") // 2
    }
  } catch (e: Exception) {
    Log.e("Lesson5", "Caught exception", e) // 3
  }
}

Exception Aggregation

What happens if multiple children coroutines throw exceptions at the same time? The first encountered exception wins, and the others are added as suppressed exceptions to it. Note that suppressed exceptions are a mechanism provided by the Java standard library and it isn’t specific to Kotlin Coroutines.

See forum comments
Download course materials from Github
Previous: Introduction Next: Demo