App exits after catching exception thrown by coroutine
See original GitHub issueI’m new to Kotlin and coroutines so maybe I’m doing something wrong, but…
The test code below throws an exception which is propagated to the caller in await().
The caller catches the exception, logs it, and ignores. So far so good.
But - immediately after this, the app exits like this, looks like the exception gets rethrown.
This is quite unexpected to me (but again I’m a novice). Do exceptions propagated by await() always get rethrown? What if I want to ignore it (show message to user, log, etc. but don’t want the exception to keep bubbling up)?
GlobalScope.launch(Dispatchers.Main) {
val run = async(Dispatchers.IO) {
if (System.currentTimeMillis() != 0L) {
throw IOException("Test")
}
"foobar"
}
try {
val res = run.await()
Log.i(TAG, "Result: " + res)
} catch (x: Throwable) {
Log.w(TAG, "Error:", x)
}
}
W MainActivity: Error:
W MainActivity: java.io.IOException: Test
W MainActivity: at org.kman.updatechecker.MainActivity$startCheckJob$2$run$1.invokeSuspend(MainActivity.kt:99)
W MainActivity: at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)
W MainActivity: at kotlinx.coroutines.DispatchedTask$DefaultImpls.run(Dispatched.kt:221)
W MainActivity: at kotlinx.coroutines.DispatchedContinuation.run(Dispatched.kt:67)
W MainActivity: at kotlinx.coroutines.scheduling.Task.run(Tasks.kt:94)
W MainActivity: at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:586)
W MainActivity: at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
W MainActivity: at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:732)
^^^^^ This is from test code's Log.w(TAG, "Error:", x)
E AndroidRuntime: FATAL EXCEPTION: main
E AndroidRuntime: Process: org.kman.updatechecker, PID: 8026
E AndroidRuntime: java.io.IOException: Test
E AndroidRuntime: at org.kman.updatechecker.MainActivity$startCheckJob$2$run$1.invokeSuspend(MainActivity.kt:99)
E AndroidRuntime: at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)
E AndroidRuntime: at kotlinx.coroutines.DispatchedTask$DefaultImpls.run(Dispatched.kt:221)
E AndroidRuntime: at kotlinx.coroutines.DispatchedContinuation.run(Dispatched.kt:67)
E AndroidRuntime: at kotlinx.coroutines.scheduling.Task.run(Tasks.kt:94)
E AndroidRuntime: at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:586)
E AndroidRuntime: at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
E AndroidRuntime: at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:732)
^^^^^ But the exception continues to bubble up
W ActivityManager: Force finishing activity org.kman.updatechecker/.MainActivity
^^^^^ The app is terminated
I’m using Android Studio 3.2, Kotlin 1.3.0-rc-190-Studio3.2-1 and kotlinx-coroutines-android 1.0.0-RC1
Issue Analytics
- State:
- Created 5 years ago
- Comments:23 (12 by maintainers)
Top Results From Across the Web
Throwing exception with coroutine crashes app instead of ...
when im throwing the exception in the CoroutineExceptionHandler my app is still crashing (with the same stack trace as before). I want to...
Read more >Exceptions in coroutines. Cancellation and Exceptions in…
When a coroutine fails with an exception, it will propagate said exception up to its parent! Then, the parent will 1) cancel the...
Read more >Coroutine exceptions handling | Kotlin
CoroutineExceptionHandler is invoked only on uncaught exceptions — exceptions that were not handled in any other way.
Read more >Coroutine Exception Handling & Observability with Firebase
When an uncaught exception is thrown, its current coroutine breaks with CancellationException. By default, with the mechanism of structured concurrency, the ...
Read more >Are You Handling Exceptions in Kotlin Coroutines Properly?
If you are a Kotlin developer, you most probably know that coroutines communicate errors in execution by throwing exceptions.
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
Top Related Hashnode Post
No results found
Top GitHub Comments
@jcornaz I just tried it, yes nice that there is less “syntax noise” but (still!)…
The “recipe” you gave is very close to one found here:
https://proandroiddev.com/async-code-using-kotlin-coroutines-233d201099ff
Let me step back and restate what I’m trying to do.
Consider this all synchronous code, no coroutines, async / await’s or anything:
Things to note:
I am able to handle errors close to where they can occur, to show the most relevant message to the user (not just “error” but “error getting account / order info”);
Cancellations (without trying to continue with updating the UI which perhaps has gone away);
The operations are sequential (can’t get order object without account object), but I don’t want to make a new function that combines both and returns a single result - this is the function I’m writing here already.
The problem here is of course that getAccount / getOrder need to execute on an IO thread, and updating the UI needs to execute on the “main” (UI, GUI toolkit) thread.
Kotlin coroutines to the rescue! I can just run some parts of my code on an IO thread and other parts on the UI thread - but write the logic as if it were sequential and synchronous.
This magically executes every piece of code on the appropriate thread. Wonderful.
But for some reason, any exceptions thrown by getAccount / getOrder are now not caught in the catch blocks around the respective await()'s.
This is “astonitishing” again, there clearly are catch blocks around the awaits, and it’s awaits which propagate exceptions, right?
Exceptions are caught only in the top-level catch (“Log / show the exception”) now and are thrown out of async{} blocks. Before adding withContext, they were getting thrown out of await()'s.
And so, new questions if you don’t mind:
Why are my exceptions now getting thrown out of async{} (vs. before withConext: out of await)?
I feel like I’m stumbling in the dark, chanting magical incantations (which sometimes work, but more often turn random objects into stones or worse)…
Are there any guides that explain in more depth what’s going on?
I have read this:
https://kotlinlang.org/docs/reference/coroutines/exception-handling.html
… but it just says that “async exposes exceptions to uses” and doesn’t explain why the introduction of withContext exposes them out of async{} and not from await(),
PS:
I see in the exception guide
https://github.com/Kotlin/kotlinx.coroutines/blob/master/docs/exception-handling.md#exception-handling
that launch “propagates exceptions automatically” but async “exposes them to users”.
Indeed, changing the code snippet to use async instead of launch makes it work as I expect:
But but but… this still seems pretty weird to me (remember I’m a newbie at Kotlin):
Yes I know it’s possible to .await() on async and its type will be just Unit which is “void” but still strange when a particular coroutine “by its nature” doesn’t return / produce anything at all (and it can be argued that “returns nothing” isn’t quite the same as “returns void”).
Since Kotlin doesn’t have checked exceptions (yes I currently use Java), when writing a coroutine we won’t be “told” by the compiler that “hey this can result in an exception, be careful”
There is no exception here. Yes one did occur, but it was caught and not re-thrown.
That’s there in the code, look, the exception does not escape, it’s done for, dead-ended:
I’m sure there are reasons why things are as they are –
– would it be possible to explain the rationale behind the “automatic rethrow even if caught and ignored” that’s done by launch? I don’t see anything in the official docs.