Cancellation is a control signal
Coroutine cancellation is cooperative. Suspending operations such as delay check it; long CPU loops should periodically call ensureActive or yield. Broad exception handling must not turn cancellation into an ordinary success or recoverable error.
import kotlinx.coroutines.*
suspend fun loadLabel(fetch: suspend () -> String): String = try {
fetch()
} catch (cancelled: CancellationException) {
throw cancelled
} catch (failure: java.io.IOException) {
"Unavailable"
}
In a regular structured scope, a failing async child cancels its parent even before anyone calls await. await also exposes the failure to its caller; the exception is not simply dormant until then. Supervision changes child-failure propagation, not the need to handle failures.
A CoroutineExceptionHandler is for uncaught exceptions in appropriate coroutine roots; it does not resume a failed coroutine or replace local recovery.
Exercise
Cancel a parent while its child is suspended and verify the fallback above is not returned. Then make fetch throw IOException and expect Unavailable.