I am wondering about handling exceptions (throwabl...
# coroutines
s
I am wondering about handling exceptions (throwables) in this example:
Copy code
val xHandler = CoroutineExceptionHandler { c, e ->
    println("Handled Crash in coroutine $c")
    e.printStackTrace(System.out)
}

fun launch_handleExceptionInCoroutineExceptionHandler(): Unit = runBlocking(xHandler) {
    launch(<http://Dispatchers.IO|Dispatchers.IO> + xHandler) {
        delay(1000)
        throw Exception("Some Exception 1")
    }
    delay(2000)
}

fun main() {
    Thread.setDefaultUncaughtExceptionHandler { t, e ->
        println("App Crash in thread $t")
        e.printStackTrace(System.out)
    }
    launch_handleExceptionInCoroutineExceptionHandler()
}
The
xHandler
is never called. The output is produced by the default-uncaught-exception-handler instead. I expected the
xHandler
to be called. If I provide another scope to the
launch
, eg
GlobalScope.launch { ... }
it works as expected and the
xHandler
is called. Why is the
xHandler
not called when using the `runBlocking`’s scope? (just to be sure, I provided the
xHandler
in 2 places, when calling
runBlocking
and when calling `launch`…)
Is a CoroutineExceptionHandler in child-coroutinescope ignored when it executes
launch
? (cc @elizarov)
e
It depends.
CoroutineExceptionHandler
is only used for uncaught exceptions. If there is a parent that handles exception, then
CoroutineExceptionHandler
is not used at all
s
Ah… thank you! A
Throwable
thrown in a a
launch
bubbles all the way up to the top-most parent coroutine-scope that has an
CoroutineExceptionHandler
, ignoring any `CoroutineExceptionHandler`s in child-coroutinescopes. Is this correct? But when calling
runBlocking
, its
CoroutineScope
does not seem to have a
CoroutineExceptionHandler
, because it the code-example, the ‘default-uncaught-exception-handler’ is invoked.
e
Yes
It bubbles up to the topmost parent and
CoroutineExceptionHandler
of that parent is used
However, “scoped parents” like
coroutinesScope
and
runBlocking
don’t use
CoroutineExceptionHandler
at all! They simply throw the corresponding exception up the call stack
s
Great! If I understand correctly, that means that if your code needs to handle exceptions ‘locally’, use a
try { ... } catch(...) { ... }
block in your
launch
. For the
runBlocking
(and
coroutineScope
): Even if one of its child-coroutinescopes issues a
launch
with a context that does have a
CoroutineExceptionHandler
, they’ll throw it up the call-stack?
e
1. Yes. You should do
launch { try { ... } catch(...) { ... } }
2. Yes. So you can do
try { coroutineScope { ... } } catch(...) { ... }
s
Thank you, Roman! I really appreciate your help
@elizarov If the parent coroutine-scope handles the exception, then
xHandlerParent
should handle it, not
xParentLeaf
as in the example below. The thrown Exception is handled by `xHandlerLeaf`… Shouldn’t it be
xHandlerParent
?
Copy code
fun launch_handleExceptionUsingCoroutineExceptionHandler(): Unit {
    CoroutineScope(xHandlerParent).launch(xHandlerLeaf) {
        delay(1000)
        throw Exception("Some Exception 1")
    }

    Thread.sleep(2000)
}
However, in this example, xHandlerParent does handle the exception:
Copy code
fun launch_handleExceptionUsingCoroutineExceptionHandler(): Unit {
    CoroutineScope(Dispatchers.Default).launch(xHandlerParent) {
        launch(xHandlerLeaf) {
            delay(1000)
            throw Exception("Some Exception 1")
        }
    }

    Thread.sleep(2000)
}