kotlinx.coroutines: Help newbies to handle exceptions in coroutines

I can’t figure how to catch the exceptions thrown from a launch block. I don’t understand how to use the CoroutineExceptionHandler. I think that the Try pattern is quite heavy (https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/src/test/kotlin/kotlinx/coroutines/experimental/Try.kt). The CompletableFuture wrapper works as expected (simply), but it requires the jdk8 lib. Thanks for this masterpiece !

import kotlinx.coroutines.experimental.delay
import kotlinx.coroutines.experimental.future.await
import kotlinx.coroutines.experimental.future.future
import kotlinx.coroutines.experimental.launch
import kotlinx.coroutines.experimental.runBlocking
import org.junit.Ignore
import org.junit.Test
import java.util.concurrent.atomic.AtomicBoolean

class CoroutinesTest {

    class TestException: Exception("test exception")

    @Test
    @Ignore("not that way...")
    fun testExceptionOnLaunch_tryCatch() {
        runBlocking {
            val caught = AtomicBoolean(false)
            val job = launch(context) {
                delay(10)
                throw TestException()
            }
            try {
                job.join()
            } catch (e: TestException) {
                caught.set(true)
            } finally {
                assert(caught.get())
            }
        }
    }

    @Test
    @Ignore("not that way...")
    fun testExceptionOnLaunch_getCompletionException() {
        runBlocking {
            val job = launch(context) {
                delay(10)
                throw TestException()
            }
            try {
                job.join()
            } finally {
                val exc = job.getCompletionException()
                assert(exc is TestException)
            }
        }
    }

    @Test
    fun testExceptionOnFuture() {
        System.setProperty("kotlinx.coroutines.debug", "off")
        runBlocking {
            val caught = AtomicBoolean(false)
            val job = future(context) {
                delay(10)
                throw TestException()
            }
            try {
                job.await()
            } catch (e: TestException) {
                caught.set(true)
            } finally {
                assert(caught.get())
            }
        }
    }
}

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 7
  • Comments: 25 (8 by maintainers)

Most upvoted comments

Honestly Kotlin seems to be a crap in the long term, more like wheel reinventing. Didn’t use beautiful features of C#, Java, just created a new way of doing everything things.

So kotlin is trying to say that, i cant handle error like the popular Promise way? example Defered.onError(//pass a lambda here),

The learning curve is fucking high, Docs didnt explain things well. all i gain is

  1. Null Safety(Java is already doing well with that)
  2. Concise (Still difficult to read a Kotlin Code, coming from a static typing world)
  3. No Performance Gain
  4. No Domain Specific Feature(like Scala in Distributed Computing)

I really want to help in the Docs, fuck coroutines. (Not really happy with this kotlin, I feeling threatened my Java was getting obsolete and wasted my time learning kotlin)

I still Love The Challenge Kotlin is giving me though, good work guys 😃

You can switch to async instead of launch and use await instead of join. This way exceptions will percolated to the functions that await. I’ll keep this issue open, because there is definitely a need for a more detailed explanation in docs.

Hi @Zedonboy

So kotlin is trying to say that, i cant handle error like the popular Promise way?

fun <T> Deferred<T>.onError(block: suspend (Exception) -> Unit): Deferred<T> {
    launch {
        try {
            await()
        } catch (e: Exception) {
            block(e)
        }
    }
    return this
}

Have fun

@pdegand Just replace launch with async in the above above code and you’ll get what you are looking for:

The recommended way, for now, is to use try { ... } catch { ... } in the code of the coroutine. We might consider adding helper function to restart coroutine on exception in the future. Please, create a separate issue if you have any specific ideas on how this kind of API might look like.

Thanks @fvasco , sometimes it takes time to learn a new language and master. My comfort zone was Javascript. Now working on a production grade Android Software with Kotlin. (Finally am falling in love)

Keep up the good work guys.

Hi @Zedonboy

So kotlin is trying to say that, i cant handle error like the popular Promise way?

fun <T> Deferred<T>.onError(block: suspend (Exception) -> Unit): Deferred<T> {
    launch {
        try {
            await()
        } catch (e: Exception) {
            block(e)
        }
    }
    return this
}

Have fun

can you give an example of this extension function in action?

Feel free to lift Arrow’s extension functions for this: https://github.com/arrow-kt/arrow/blob/master/modules/effects/arrow-effects-kotlinx-coroutines/src/main/kotlin/arrow/effects/DeferredK.kt

Replace Try with try/catch and a function catch block and it should work the same.

@elizarov I’ve investigated some more and there are some interesting things happening. For testing I’ve set up the following Activity in an newly created project:

class MainActivity : AppCompatActivity() {

    val UI = kotlinx.coroutines.experimental.android.UI

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        testButton.setOnClickListener {
            launch(UI) {
                crashingFunction()
            }
        }
    }

    private fun crashingFunction(): Int {
        return 23.div(0)
    }
}

On a Sony Xperia Z3 API 23 everything works as intended. The app is crashing and the stacktrace is printed to Logcat.

But run the same code on a Samsung S8 API 26 and the app is crashing but there is no stacktrace in Logcat. This is 100% reproducable but I don’t know where’s the root error causing this. Maybe we should create a seperate issue for that?

To answer your question what I want to archieve: If there is an exception in launch, I want the app to crash and the stacktrace printed into Logcat, so that the other developers and I know what we’ve done wrong during development (the printing is even more important than the crashing).

For now, I’m using your approach which works on API 26, too:

val UI = kotlinx.coroutines.experimental.android.UI + CoroutineExceptionHandler { _, t ->
    launch(kotlinx.coroutines.experimental.android.UI) { throw t }
}

Or are there any drawbacks I’m not aware of with this solution?

PS: I’m a big fan of your work and I want to use them as an integral part of our application, but I also have to convince my superiors that they work better than conventional handlers and threads 🤖

EDIT: You must launch another coroutine with context kotlinx.coroutines.experimental.android.UIto actually throw the exception on the main thread and get a crash

@MariusBoepple Uncaught exceptions in launch are designed to crash your app. If you want to handle them different, you should either wrap your code with try { ... } catch { ... } or create your own context with CoroutineExceptionHandler like this:

// define your own UI context
val UI = kotlinx.coroutines.experimental.android.UI + CoroutineExceptionHandler { _, e -> 
    println(e) // don't crash, just print exception -> DON'T REALLY DO IT LIKE THIS
}

private fun check() {
    launch(UI) {
        InetAddress.getByName(hostName).isReachable(1000)
    }
}

I’m curious at what exactly you are trying to achieve? What do you want to do on exception?

@elizarov the following code does just crash without logging anything (NetworkOnMainThreadException). We stumble over the exception handling in the coroutines all the time and it would be nice to have a more convenient solution. Or am I doing something wrong there?

private fun check() = launch(UI) {
    async(UI) {
        InetAddress.getByName(hostName).isReachable(1000)
    }.await()
}

This doesn’t log anything either:

private fun check() {
    launch(UI) {
        InetAddress.getByName(hostName).isReachable(1000)
    }.invokeOnCompletion { e -> e?.let { throw e } }
}

Yeah i finally mastered Kotlin, i asked this when i was still wrapping my head round Kotlin. And frustration came in 😃

On Fri, Oct 26, 2018, 3:08 PM Ebenezer notifications@github.com wrote:

Hi @Zedonboy https://github.com/Zedonboy

So kotlin is trying to say that, i cant handle error like the popular Promise way?

fun <T> Deferred<T>.onError(block: suspend (Exception) -> Unit): Deferred<T> { launch { try { await() } catch (e: Exception) { block(e) } } return this }

Have fun

can you give an example of this extension function in action?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/Kotlin/kotlinx.coroutines/issues/61#issuecomment-433420117, or mute the thread https://github.com/notifications/unsubscribe-auth/AVIoqJuDm2IRsj9sgrvk5qtQfWapkUrvks5uoxdLgaJpZM4NgskM .

I’m closing this, because new “Exceptions” section in coroutines guide should address all the basic newbie questions.

Much easier it just using deferred.await(). It will throw on failure and the exception (and failure) will get logged. You can do .forEach { it.await() } to get a test failure if any of them fails