1<!--- TEST_NAME ExceptionsGuideTest --> 2 3**Table of contents** 4 5<!--- TOC --> 6 7* [Exception Handling](#exception-handling) 8 * [Exception propagation](#exception-propagation) 9 * [CoroutineExceptionHandler](#coroutineexceptionhandler) 10 * [Cancellation and exceptions](#cancellation-and-exceptions) 11 * [Exceptions aggregation](#exceptions-aggregation) 12 * [Supervision](#supervision) 13 * [Supervision job](#supervision-job) 14 * [Supervision scope](#supervision-scope) 15 * [Exceptions in supervised coroutines](#exceptions-in-supervised-coroutines) 16 17<!--- END --> 18 19## Exception Handling 20 21This section covers exception handling and cancellation on exceptions. 22We already know that a cancelled coroutine throws [CancellationException] in suspension points and that it 23is ignored by the coroutines' machinery. Here we look at what happens if an exception is thrown during cancellation or multiple children of the same 24coroutine throw an exception. 25 26### Exception propagation 27 28Coroutine builders come in two flavors: propagating exceptions automatically ([launch] and [actor]) or 29exposing them to users ([async] and [produce]). 30When these builders are used to create a _root_ coroutine, that is not a _child_ of another coroutine, 31the former builders treat exceptions as **uncaught** exceptions, similar to Java's `Thread.uncaughtExceptionHandler`, 32while the latter are relying on the user to consume the final 33exception, for example via [await][Deferred.await] or [receive][ReceiveChannel.receive] 34([produce] and [receive][ReceiveChannel.receive] are covered later in [Channels](https://github.com/Kotlin/kotlinx.coroutines/blob/master/docs/channels.md) section). 35 36It can be demonstrated by a simple example that creates root coroutines using the [GlobalScope]: 37 38<div class="sample" markdown="1" theme="idea" data-highlight-only> 39 40```kotlin 41import kotlinx.coroutines.* 42 43fun main() = runBlocking { 44 val job = GlobalScope.launch { // root coroutine with launch 45 println("Throwing exception from launch") 46 throw IndexOutOfBoundsException() // Will be printed to the console by Thread.defaultUncaughtExceptionHandler 47 } 48 job.join() 49 println("Joined failed job") 50 val deferred = GlobalScope.async { // root coroutine with async 51 println("Throwing exception from async") 52 throw ArithmeticException() // Nothing is printed, relying on user to call await 53 } 54 try { 55 deferred.await() 56 println("Unreached") 57 } catch (e: ArithmeticException) { 58 println("Caught ArithmeticException") 59 } 60} 61``` 62 63</div> 64 65> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-exceptions-01.kt). 66 67The output of this code is (with [debug](https://github.com/Kotlin/kotlinx.coroutines/blob/master/docs/coroutine-context-and-dispatchers.md#debugging-coroutines-and-threads)): 68 69```text 70Throwing exception from launch 71Exception in thread "DefaultDispatcher-worker-2 @coroutine#2" java.lang.IndexOutOfBoundsException 72Joined failed job 73Throwing exception from async 74Caught ArithmeticException 75``` 76 77<!--- TEST EXCEPTION--> 78 79### CoroutineExceptionHandler 80 81It is possible to customize the default behavior of printing **uncaught** exceptions to the console. 82[CoroutineExceptionHandler] context element on a _root_ coroutine can be used as generic `catch` block for 83this root coroutine and all its children where custom exception handling may take place. 84It is similar to [`Thread.uncaughtExceptionHandler`](https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#setUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler)). 85You cannot recover from the exception in the `CoroutineExceptionHandler`. The coroutine had already completed 86with the corresponding exception when the handler is called. Normally, the handler is used to 87log the exception, show some kind of error message, terminate, and/or restart the application. 88 89On JVM it is possible to redefine global exception handler for all coroutines by registering [CoroutineExceptionHandler] via 90[`ServiceLoader`](https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html). 91Global exception handler is similar to 92[`Thread.defaultUncaughtExceptionHandler`](https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#setDefaultUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler)) 93which is used when no more specific handlers are registered. 94On Android, `uncaughtExceptionPreHandler` is installed as a global coroutine exception handler. 95 96`CoroutineExceptionHandler` is invoked only on **uncaught** exceptions — exceptions that were not handled in any other way. 97In particular, all _children_ coroutines (coroutines created in the context of another [Job]) delegate handling of 98their exceptions to their parent coroutine, which also delegates to the parent, and so on until the root, 99so the `CoroutineExceptionHandler` installed in their context is never used. 100In addition to that, [async] builder always catches all exceptions and represents them in the resulting [Deferred] object, 101so its `CoroutineExceptionHandler` has no effect either. 102 103> Coroutines running in supervision scope do not propagate exceptions to their parent and are 104excluded from this rule. A further [Supervision](#supervision) section of this document gives more details. 105 106<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3"> 107 108```kotlin 109import kotlinx.coroutines.* 110 111fun main() = runBlocking { 112//sampleStart 113 val handler = CoroutineExceptionHandler { _, exception -> 114 println("CoroutineExceptionHandler got $exception") 115 } 116 val job = GlobalScope.launch(handler) { // root coroutine, running in GlobalScope 117 throw AssertionError() 118 } 119 val deferred = GlobalScope.async(handler) { // also root, but async instead of launch 120 throw ArithmeticException() // Nothing will be printed, relying on user to call deferred.await() 121 } 122 joinAll(job, deferred) 123//sampleEnd 124} 125``` 126 127</div> 128 129> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-exceptions-02.kt). 130 131The output of this code is: 132 133```text 134CoroutineExceptionHandler got java.lang.AssertionError 135``` 136 137<!--- TEST--> 138 139### Cancellation and exceptions 140 141Cancellation is closely related to exceptions. Coroutines internally use `CancellationException` for cancellation, these 142exceptions are ignored by all handlers, so they should be used only as the source of additional debug information, which can 143be obtained by `catch` block. 144When a coroutine is cancelled using [Job.cancel], it terminates, but it does not cancel its parent. 145 146<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3"> 147 148```kotlin 149import kotlinx.coroutines.* 150 151fun main() = runBlocking { 152//sampleStart 153 val job = launch { 154 val child = launch { 155 try { 156 delay(Long.MAX_VALUE) 157 } finally { 158 println("Child is cancelled") 159 } 160 } 161 yield() 162 println("Cancelling child") 163 child.cancel() 164 child.join() 165 yield() 166 println("Parent is not cancelled") 167 } 168 job.join() 169//sampleEnd 170} 171``` 172 173</div> 174 175> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-exceptions-03.kt). 176 177The output of this code is: 178 179```text 180Cancelling child 181Child is cancelled 182Parent is not cancelled 183``` 184 185<!--- TEST--> 186 187If a coroutine encounters an exception other than `CancellationException`, it cancels its parent with that exception. 188This behaviour cannot be overridden and is used to provide stable coroutines hierarchies for 189[structured concurrency](https://github.com/Kotlin/kotlinx.coroutines/blob/master/docs/composing-suspending-functions.md#structured-concurrency-with-async). 190[CoroutineExceptionHandler] implementation is not used for child coroutines. 191 192> In these examples [CoroutineExceptionHandler] is always installed to a coroutine 193that is created in [GlobalScope]. It does not make sense to install an exception handler to a coroutine that 194is launched in the scope of the main [runBlocking], since the main coroutine is going to be always cancelled 195when its child completes with exception despite the installed handler. 196 197The original exception is handled by the parent only when all its children terminate, 198which is demonstrated by the following example. 199 200<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3"> 201 202```kotlin 203import kotlinx.coroutines.* 204 205fun main() = runBlocking { 206//sampleStart 207 val handler = CoroutineExceptionHandler { _, exception -> 208 println("CoroutineExceptionHandler got $exception") 209 } 210 val job = GlobalScope.launch(handler) { 211 launch { // the first child 212 try { 213 delay(Long.MAX_VALUE) 214 } finally { 215 withContext(NonCancellable) { 216 println("Children are cancelled, but exception is not handled until all children terminate") 217 delay(100) 218 println("The first child finished its non cancellable block") 219 } 220 } 221 } 222 launch { // the second child 223 delay(10) 224 println("Second child throws an exception") 225 throw ArithmeticException() 226 } 227 } 228 job.join() 229//sampleEnd 230} 231``` 232 233</div> 234 235> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-exceptions-04.kt). 236 237The output of this code is: 238 239```text 240Second child throws an exception 241Children are cancelled, but exception is not handled until all children terminate 242The first child finished its non cancellable block 243CoroutineExceptionHandler got java.lang.ArithmeticException 244``` 245<!--- TEST--> 246 247### Exceptions aggregation 248 249When multiple children of a coroutine fail with an exception, the 250general rule is "the first exception wins", so the first exception gets handled. 251All additional exceptions that happen after the first one are attached to the first exception as suppressed ones. 252 253<!--- INCLUDE 254import kotlinx.coroutines.exceptions.* 255--> 256 257<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3"> 258 259```kotlin 260import kotlinx.coroutines.* 261import java.io.* 262 263fun main() = runBlocking { 264 val handler = CoroutineExceptionHandler { _, exception -> 265 println("CoroutineExceptionHandler got $exception with suppressed ${exception.suppressed.contentToString()}") 266 } 267 val job = GlobalScope.launch(handler) { 268 launch { 269 try { 270 delay(Long.MAX_VALUE) // it gets cancelled when another sibling fails with IOException 271 } finally { 272 throw ArithmeticException() // the second exception 273 } 274 } 275 launch { 276 delay(100) 277 throw IOException() // the first exception 278 } 279 delay(Long.MAX_VALUE) 280 } 281 job.join() 282} 283``` 284 285</div> 286 287> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-exceptions-05.kt). 288 289> Note: This above code will work properly only on JDK7+ that supports `suppressed` exceptions 290 291The output of this code is: 292 293```text 294CoroutineExceptionHandler got java.io.IOException with suppressed [java.lang.ArithmeticException] 295``` 296 297<!--- TEST--> 298 299> Note that this mechanism currently only works on Java version 1.7+. 300The JS and Native restrictions are temporary and will be lifted in the future. 301 302Cancellation exceptions are transparent and are unwrapped by default: 303 304<div class="sample" markdown="1" theme="idea" data-min-compiler-version="1.3"> 305 306```kotlin 307import kotlinx.coroutines.* 308import java.io.* 309 310fun main() = runBlocking { 311//sampleStart 312 val handler = CoroutineExceptionHandler { _, exception -> 313 println("CoroutineExceptionHandler got $exception") 314 } 315 val job = GlobalScope.launch(handler) { 316 val inner = launch { // all this stack of coroutines will get cancelled 317 launch { 318 launch { 319 throw IOException() // the original exception 320 } 321 } 322 } 323 try { 324 inner.join() 325 } catch (e: CancellationException) { 326 println("Rethrowing CancellationException with original cause") 327 throw e // cancellation exception is rethrown, yet the original IOException gets to the handler 328 } 329 } 330 job.join() 331//sampleEnd 332} 333``` 334 335</div> 336 337> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-exceptions-06.kt). 338 339The output of this code is: 340 341```text 342Rethrowing CancellationException with original cause 343CoroutineExceptionHandler got java.io.IOException 344``` 345<!--- TEST--> 346 347### Supervision 348 349As we have studied before, cancellation is a bidirectional relationship propagating through the whole 350hierarchy of coroutines. Let us take a look at the case when unidirectional cancellation is required. 351 352A good example of such a requirement is a UI component with the job defined in its scope. If any of the UI's child tasks 353have failed, it is not always necessary to cancel (effectively kill) the whole UI component, 354but if UI component is destroyed (and its job is cancelled), then it is necessary to fail all child jobs as their results are no longer needed. 355 356Another example is a server process that spawns multiple child jobs and needs to _supervise_ 357their execution, tracking their failures and only restarting the failed ones. 358 359#### Supervision job 360 361The [SupervisorJob][SupervisorJob()] can be used for these purposes. 362It is similar to a regular [Job][Job()] with the only exception that cancellation is propagated 363only downwards. This can easily be demonstrated using the following example: 364 365<div class="sample" markdown="1" theme="idea" data-highlight-only> 366 367```kotlin 368import kotlinx.coroutines.* 369 370fun main() = runBlocking { 371 val supervisor = SupervisorJob() 372 with(CoroutineScope(coroutineContext + supervisor)) { 373 // launch the first child -- its exception is ignored for this example (don't do this in practice!) 374 val firstChild = launch(CoroutineExceptionHandler { _, _ -> }) { 375 println("The first child is failing") 376 throw AssertionError("The first child is cancelled") 377 } 378 // launch the second child 379 val secondChild = launch { 380 firstChild.join() 381 // Cancellation of the first child is not propagated to the second child 382 println("The first child is cancelled: ${firstChild.isCancelled}, but the second one is still active") 383 try { 384 delay(Long.MAX_VALUE) 385 } finally { 386 // But cancellation of the supervisor is propagated 387 println("The second child is cancelled because the supervisor was cancelled") 388 } 389 } 390 // wait until the first child fails & completes 391 firstChild.join() 392 println("Cancelling the supervisor") 393 supervisor.cancel() 394 secondChild.join() 395 } 396} 397``` 398 399</div> 400 401> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-supervision-01.kt). 402 403The output of this code is: 404 405```text 406The first child is failing 407The first child is cancelled: true, but the second one is still active 408Cancelling the supervisor 409The second child is cancelled because the supervisor was cancelled 410``` 411<!--- TEST--> 412 413 414#### Supervision scope 415 416Instead of [coroutineScope][_coroutineScope], we can use [supervisorScope][_supervisorScope] for _scoped_ concurrency. It propagates the cancellation 417in one direction only and cancels all its children only if it failed itself. It also waits for all children before completion 418just like [coroutineScope][_coroutineScope] does. 419 420<div class="sample" markdown="1" theme="idea" data-highlight-only> 421 422```kotlin 423import kotlin.coroutines.* 424import kotlinx.coroutines.* 425 426fun main() = runBlocking { 427 try { 428 supervisorScope { 429 val child = launch { 430 try { 431 println("The child is sleeping") 432 delay(Long.MAX_VALUE) 433 } finally { 434 println("The child is cancelled") 435 } 436 } 437 // Give our child a chance to execute and print using yield 438 yield() 439 println("Throwing an exception from the scope") 440 throw AssertionError() 441 } 442 } catch(e: AssertionError) { 443 println("Caught an assertion error") 444 } 445} 446``` 447 448</div> 449 450> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-supervision-02.kt). 451 452The output of this code is: 453 454```text 455The child is sleeping 456Throwing an exception from the scope 457The child is cancelled 458Caught an assertion error 459``` 460<!--- TEST--> 461 462#### Exceptions in supervised coroutines 463 464Another crucial difference between regular and supervisor jobs is exception handling. 465Every child should handle its exceptions by itself via the exception handling mechanism. 466This difference comes from the fact that child's failure does not propagate to the parent. 467It means that coroutines launched directly inside the [supervisorScope][_supervisorScope] _do_ use the [CoroutineExceptionHandler] 468that is installed in their scope in the same way as root coroutines do 469(see the [CoroutineExceptionHandler](#coroutineexceptionhandler) section for details). 470 471<div class="sample" markdown="1" theme="idea" data-highlight-only> 472 473```kotlin 474import kotlin.coroutines.* 475import kotlinx.coroutines.* 476 477fun main() = runBlocking { 478 val handler = CoroutineExceptionHandler { _, exception -> 479 println("CoroutineExceptionHandler got $exception") 480 } 481 supervisorScope { 482 val child = launch(handler) { 483 println("The child throws an exception") 484 throw AssertionError() 485 } 486 println("The scope is completing") 487 } 488 println("The scope is completed") 489} 490``` 491 492</div> 493 494> You can get the full code [here](../kotlinx-coroutines-core/jvm/test/guide/example-supervision-03.kt). 495 496The output of this code is: 497 498```text 499The scope is completing 500The child throws an exception 501CoroutineExceptionHandler got java.lang.AssertionError 502The scope is completed 503``` 504<!--- TEST--> 505 506<!--- MODULE kotlinx-coroutines-core --> 507<!--- INDEX kotlinx.coroutines --> 508[CancellationException]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-cancellation-exception/index.html 509[launch]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html 510[async]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html 511[Deferred.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/await.html 512[GlobalScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-global-scope/index.html 513[CoroutineExceptionHandler]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-exception-handler/index.html 514[Job]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html 515[Deferred]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/index.html 516[Job.cancel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/cancel.html 517[runBlocking]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html 518[SupervisorJob()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-supervisor-job.html 519[Job()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job.html 520[_coroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html 521[_supervisorScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/supervisor-scope.html 522<!--- INDEX kotlinx.coroutines.channels --> 523[actor]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/actor.html 524[produce]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/produce.html 525[ReceiveChannel.receive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/receive.html 526<!--- END --> 527