-
Notifications
You must be signed in to change notification settings - Fork 1.9k
CoroutineExceptionHandler: Issues with Documentation, Possible Bug in runBlocking #1746
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
The docs are indeed vague there, but it seems to me that In this particular example there's no unhandled exception at all. |
There is often confusion as to how exactly the exception handler works. There is a steady trickle of questions on Stack Overflow and I basically had to infer the rule myself, it wasn't clearly spelled out. I intended this primarily as a documentation issue. |
@mtopolnik Here's my attempt and improving the corresponding docs to reduce confusion: #1886 |
* Further clarifications and better style for exception handling * Consistent terminology on "uncaught exceptions". * Clarified special relations of exception handling with supervision. * Clearer text in CoroutineExceptionHandler examples. * Minor stylistic corrections. Fixes #1746 Fixes #871 Co-Authored-By: EdwarDDay <[email protected]>
* Further clarifications and better style for exception handling * Consistent terminology on "uncaught exceptions". * Clarified special relations of exception handling with supervision. * Clearer text in CoroutineExceptionHandler examples. * Minor stylistic corrections. Fixes Kotlin#1746 Fixes Kotlin#871 Co-Authored-By: EdwarDDay <[email protected]>
Hi, it's still unclear to me. The exception always travels up the coroutine hierarchy to its root. Only at the root it can become an "unhandled exception" in which case the root's CoroutineExceptionHandler is used. Does it mean that runBlocking intentionally handles exceptions by throwing them immediately i.e. it fits into the whole mechanism but as root it already has a defined behavior which is re-throwing and that's why Or it means that |
Yes, this. |
Given this code:
When I run it like this (using the scope constructed above):
I get the expected output:
However, when I run it like this (using the
runBlocking
scope):the handler doesn't observe the exception and I get the default handler's output instead:
Even when I add the handler to
runBlocking
:it still crashes without the handler observing the exception.
The behavior is not specific to
runBlocking
, I can write the code as follows:and I still get just a crash (now inside the
Default
thread pool).Finally, if I write it as follows (adding the handler to the top-level
launch
):this time it gets handled.
After studying the documentation on
CoroutineExceptionHandler
in detail, I found the above behaviors surprising. The documentation explicitly specifies only what happens when a coroutine exception handler is not installed, and I could only indirectly guess at what it was supposed to do when there is one. Yet my guesses were all wrong. Paraphrasing, the docs seem to claim the following:CancellationException
.Job
in the context, invokeJob.cancel
.The actual behavior is more like the following:
coroutineContext[Job]!!.cancel()
(since there is always aJob
in any coroutine's context)a. Never handle a
CancellationException
.b. If the current coroutine has a parent coroutine, don't handle the exception. it was passed to the parent via
job.cancel()
.c. Otherwise pass the exception to the exception handler.
Furthermore, the behavior with
runBlocking
seems like a bug to fix.The text was updated successfully, but these errors were encountered: