We have already seen Exception handling using try-catch
blocks, but they don’t seem to work with coroutines. Let’s see how. Observe the following program :
fun main() {
runBlocking {
try {
launch {
error("Something went wrong!")
}
} catch (e: Exception) {
println("ERROR : ${e.message}")
}
}
}
/* Output :
Exception in thread "main" java.lang.IllegalStateException: Something went wrong!
*/
Even though errorenous code is wrapped in try-catch
block, the error goes unhandled, application is crashed and stacktrace is printed. Note that here launch()
is wrapped in try-catch
. Let’s try the other way around i.e. try-catch
inside launch()
.
fun main() {
runBlocking {
launch {
try {
error("Something went wrong!")
} catch (e: Exception) {
println("ERROR : ${e.message}")
}
}
}
}
// Output : ERROR : Something went wrong!
This time the error was correctly handled by the try-catch
block and the custom error message ERROR : Something went wrong!
got printed.
Why is this so? Why is try-catch
block unable to handle errors thrown by coroutines launched inside it? This is because launch()
does not execute the input lambda directly. Based on the CoroutineContext & Dispatcher, the lambda execution is scheduled internally. So try-catch
doesn’t work on the lambda passed. What’s the solution then? Well, one simple solution we just saw is to have try-catch
inside launch()
. But this isn’t reliable as it won’t be able to catch errors of child coroutines. Example :
fun main() {
runBlocking {
// Main coroutine
launch {
try {
// Child coroutine
launch {
// Won't be caught
error("(Child) Something went wrong!")
}
delay(100)
// Can be caught
error("(Main) Something went wrong!")
} catch (e: IllegalStateException) {
println("ERROR : ${e.message}")
}
}
}
}
/* Output :
ERROR : StandaloneCoroutine is cancelling
Exception in thread "main" java.lang.IllegalStateException: (Child) Something went wrong! */
Note :
- Child coroutine’s
error()
statement is executed before before theerror()
statement of main coroutine due to the added delay. - It’s clear from the output that child coroutine’s error is not caught, application is crashed and stacktrace is printed.
To solve this problem, if we keep wrapping the code of each coroutine in try-catch
blocks, our code will get messy. So, we won’t be using try-catch
when dealing with exceptions inside coroutines. Instead, the best solution i.e. CoroutineExceptionHandler will be used.