However, there are situations where errors can get lost in coroutines.
val unrelatedScope = MainScope() // example of a lost error suspend fun lostError() { // async without structured concurrency unrelatedScope.async { throw InAsyncNoOneCanHearYou("except") } }
Note this code is declaring an unrelated coroutine scope that will launch a new coroutine without structured concurrency. Remember at the beginning I said that structured concurrency is a combination of types and programming practices, and introducing unrelated coroutine scopes in suspend functions is not following the programming practices of structured concurrency.
The error is lost in this code because async assumes that you will eventually call await where it will rethrow the exception. However, if you never do call await, the exception will be stored forever waiting patiently waiting to be raised.
Structured concurrency guarantees that when a coroutine errors, its caller or scope is notified.
If you do use structured concurrency for the above code, the error will correctly be thrown to the caller.
suspend fun foundError() { coroutineScope { async { throw StructuredConcurrencyWill("throw") } } }