What happens to a coroutine when an exception occurs?
sobes.tech AI
Answer from AI
When an exception occurs inside a coroutine, the dependency on the exception type (and dispatcher/context settings) determines how it will be handled.
-
Uncaught exception: If an exception is not caught inside a coroutine, it propagates up the coroutine hierarchy to the nearest parent
Job(orCoroutineScope). The behavior depends on the type ofJob:Job: By default, if an exception occurs in a child coroutine, the parentJobis canceled, which leads to the cancellation of all its other child coroutines. The exception is rethrown upwards.SupervisorJob: The parentSupervisorJobis not canceled when an exception occurs in a child coroutine. Only the coroutine where the exception occurred is canceled. This is useful when you want the cancellation of one child task not to affect others.- Root
CoroutineScope: If the exception reaches the rootCoroutineScope(for example,GlobalScopeor a scope created withCoroutineScopewithout aJob), it is passed to the global exception handlerCoroutineExceptionHandler. By default, if no handler is registered, the exception is logged toSystem.err.
-
Caught exception: If an exception is caught using a
try-catchblock inside a coroutine, it is handled locally, and the coroutine continues execution after thecatchblock, if possible. The exception is not propagated upwards. -
Cancellation-related exceptions (e.g.,
CancellationException): These exceptions are special. They are used for coroutine cancellation mechanisms and are usually ignored by most exception handlers by default. They do not cause a fatal failure of the parentJobby default. -
Exceptions in
async: A coroutine launched viaasyncplaces the exception in itsDeferred. The exception is rethrown only when.await()is called on thatDeferred. Until then, the exception is "hidden".
import kotlinx.coroutines.*
fun main() = runBlocking {
val scope = CoroutineScope(SupervisorJob())
scope.launch {
try {
println("Coroutine 1 started")
delay(100)
throw RuntimeException("Something went wrong in Coroutine 1")
} catch (e: Exception) {
println("Coroutine 1 caught exception: ${e.message}")
}
}
scope.launch {
println("Coroutine 2 started")
delay(500)
println("Coroutine 2 finished")
}
delay(1000) // Wait to see the output
scope.cancel() // Cancel the scope
}
In this example, the exception in Coroutine 1 is caught, and Coroutine 2 continues execution because a SupervisorJob is used. If a regular Job were used instead, the exception in Coroutine 1 would lead to the cancellation of Coroutine 2.