Rationale behind awaitAll's error handling?

For the above code I’m not sure if we have any guarantees if we get CancellationException or fake in both cases: when using awaitAll() or await().

Please be aware there are two separate error propagation mechanisms involved in this case. There is a race between them and depending which wins, you get one exception or another. First is because we invoke await()/awaitAll(), so we would like to re-throw the error from the deferred. Second is because we launched that deferred as our child, and its failures automatically cancel the parent.

Using .forEach { it.await() } doesn’t guarantee you’ll get fake. Put a long running deferred first, then the one that fails and you will get CancellationException. Which makes sense, because you don’t even await on the child that failed. Well, you don’t even have to await on anything - simply replace your current awaitAll() with delay() and you will also get CancellationException. Maybe there is an opposite guarantee to get CancellationException if using awaitAll(), but my guess would be that it may throw fake as well.

If you don’t like the behavior of errors automatically propagating from children to parent, simply use supervisorScope. Then awaitAll() is guaranteed to throw fake from the above code.

Also, please be aware if you don’t plan to use the value returned from await()/awaitAll(), then you don’t need to even call it. coroutineScope() automatically waits for any coroutines launched inside it. We don’t have to wait on them explicitly.