I've been having problems figuring out error handling with coroutines that I've narrowed down to this unit test with the following steps:
- I create a coroutine scope, with any dispatcher.
- I throw an exception anywhere within this scope in an async block (or even in a nested async block).
- I call await on the returned deferred value and handle the exception.
This is all fine. However, when I try to use the same coroutine scope to launch a new coroutine, this always completes exceptionally with the same exception.
Here is the test:
fun `when you throw an exception in a coroutine scope, is the coroutine scope dead?`() { val parentJob = Job() val coroutineScope = CoroutineScope(parentJob + Dispatchers.Default) val deferredResult = coroutineScope.async { throw IllegalStateException() } runBlocking { try { deferredResult.await() } catch (e: IllegalStateException) { println("We caught the exception. Good.") } try { coroutineScope.async { println("we can still use the scope") }.await() } catch (e: IllegalStateException) { println("Why is this same exception still being thrown?") } } }
Here is the output of the test:
We caught the exception. Good.
Why is this same exception still being thrown?
Why is this happening?
- My understanding was that you could handle exceptions normally and recover from them with coroutines.
How should I deal with exceptions?
- Do I need to create a new coroutineScope?
- Can I never throw exceptions if I want to keep using the same coroutineScope?
- Should I return
Either<Result, Exception>
? - I've tried using CoroutineExceptionHandler but I still get the same results.
Note I'm using Kotlin 1.3
SupervisorJob
asparentJob
. – Pawel