Exception handling with in a coroutine with a supervisor job

Hello, I’ve the following scope created in my fragment;

private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())

...

       val handler = CoroutineExceptionHandler { _, exception ->
                println("TAG-Caught $exception")
       }

scope.launch(handler) {

            launch {
                println("TAG-first job is running")
                delay(200)
            }

            // second job
            testParentChildWithExceptionWithSupervision()

            launch {
                println("TAG-third job is running")
            }
        }

then the function testParentChildWithExceptionWithSupervision looks like;

suspend fun testParentChildWithExceptionWithSupervision() {

    supervisorScope {

        val job1 = launch {
            println("TAG-running first (inner) child")
            delay(200)
            throw ArithmeticException()
        }

        val job2 = launch {
            job1.join()
            println("TAG-First child is cancelled: ${job1.isCancelled}, but second one is still active")
            delay(200)
        }

        job1.join()
        println("TAG-second child is cancelled: ${job2.isCancelled}")

        println("TAG-ENDING")
    }
}

The output is like;

The point that I don’t quite get is about a part in the docs seems like doesn’t fit with what’s being executed;

Another crucial difference between regular and supervisor jobs is exception handling. Every child should handle its exceptions by itself via exception handling mechanisms. This difference comes from the fact that child’s failure is not propagated to the parent.

I see that the exception is still propagated upwards. Shouldn’t it be handled in the method contains the supervisorScope and not be propagated back to the fragment?

Please consider this example:

import kotlinx.coroutines.*

fun main() {
    runBlocking {
        supervisorScope { // parent
            launch { error("Works") } // child
        }

        coroutineScope { // parent
            launch { error("Crash") } // child
        }
    }
}

Child’s exception is not thrown by supervisorScope to the parent.
Instead the coroutineScope throws the unhandled exception and the program crashes.

I don’t think this what I’m asking.

For instance, you can still catch the exception using one another on the call site, using a generic handler. But then with coroutineScope, your child jobs don’t carry on after generic exception is handled via handler.

And in case supervisorScope, what is not propagated is the cancellation. I can still handle the exception (and I should if noone does).

Hi @zgulser,
can you write a simple POC to show what do you expect and what is the unexpected result?

I don’t think to be the best person to talk about it, but we can spent some messages on it anyway.

When the docs say the child’s failure (not “exception”) is not propagated to the parent, it means it doesn’t cause the parent scope to be cancelled.

When the exception is thrown, it still looks through the scope parent hierarchy to see if there is a CoroutineExceptionHandler to pass the exception to.