Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin async in coroutine: exception caught and still propagated?

I am running a coroutine in a SupervisorJob with a try/catch block surrounding only the await calls. The exception from the async block gets caught by the try/catch, but it still gets propagated and the app crashes.

This is what I have:

CoroutineScope(Dispatchers.IO + SupervisorJob()).launch {
            val a = async {
                delay(500)
                throw Exception("excep a")
                2
            }
            val b = async {
                delay(500)
                3
            }
            try {
                println(a.await() + b.await())
            } catch (e: Exception) {
                println("exception: ${e.message}")
            }
        }

This is what I get (note that "excep a" gets caught):

exception: excep a
Exception in thread "DefaultDispatcher-worker-3 @coroutine#1" java.lang.Exception: excep a
    at com.example.app.AuthTest$co2$1$a$1.invokeSuspend(AuthTest.kt:314)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
like image 920
hoshiKuzu Avatar asked Oct 24 '25 05:10

hoshiKuzu


2 Answers

The exception handling mechanism of supervisorJob and Job is not the same. For supervisory tasks, the transmission of exceptions can only be from the parent scope to the child scope, and the propagation direction of the exception is one-way. Therefore, it is necessary to handle exceptions by itself for monitoring the scope of opening the coroutine.

    CoroutineScope(Dispatchers.IO + SupervisorJob()).launch(CoroutineExceptionHandler { _, throwable ->
        println("$throwable")
    }) {
        val a = async {
            delay(500)
            throw Exception("excep a")
            2
        }
        val b = async {
            delay(500)
            3
        }
        println(a.await() + b.await())
    }

This exception will be handled in the top-level Job

like image 82
Future Deep Gone Avatar answered Oct 26 '25 00:10

Future Deep Gone


SupervisorJob has launch as Top coroutine and both async/await are nested coroutines.

Nested async/await always propagated up via Job hierarchy even if you use try/catch.

launch as Top coroutine require CoroutineExceptionHandler.

Alternatively you can:

  1. wrap both async and try/catch in supervisorScope
  2. wrap both async and try/catch in coroutineScope inside try/catch. But in this case second async will be cancelled after exception is thrown
like image 44
mobiledev Alex Avatar answered Oct 25 '25 23:10

mobiledev Alex



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!