A supervisor is a coroutine boundary for work that should remain independent after one child fails. Under a regular Job, an uncaught child exception cancels its parent and sibling coroutines; this is useful when concurrent tasks must produce one shared result. A supervisor instead survives child failure, lets siblings continue, and stops that failure from propagating to its parent. section 18.2.1 section 18.2.3
Keep independent work alive with supervisorScope
Wrap independently useful children in supervisorScope. Here, the heartbeat continues after the other launch throws: runBlocking { supervisorScope { launch { while (true) { println("Heartbeat!") delay(500.milliseconds) } } launch { delay(1.seconds) throw UnsupportedOperationException("Ow!") } } } supervisorScope is analogous to coroutineScope, but a failed child neither terminates its siblings nor cancels the enclosing parent. The failed launch can still report its uncaught exception through a CoroutineExceptionHandler. section 18.2.3 section 18.3
Create or choose a supervising boundary when child tasks have separate lifetimes or failure policies:
- Use supervisorScope to isolate children within an existing coroutine while retaining structured concurrency. section 18.2.3
- Create a CoroutineScope with SupervisorJob when a component owns longer-running work. section 18.2.3 section 18.3
- Keep cancellation handling intact: supervisors can still be cancelled and propagate cancellation exceptions normally. section 18.2.3
- Catch exceptions inside a child when that child itself can recover; this also prevents an exception from crossing the coroutine boundary. section 18.2.2
Figure 18.2 When one or more child coroutines of a supervisor fail, the sibling and parent coroutines all keep on working. The exception is no longer propagated further.
The failure boundary is visible here: a child may fail while its supervisor, parent, and sibling coroutines continue. section 18.2.3
Supervision is not exception recovery. An uncaught exception is isolated from the parent hierarchy, but a launch child can still reach the applicable CoroutineExceptionHandler. With async, the exception is rethrown when await is called, so the awaiting code remains responsible for handling it. Framework scopes may already be supervisors; Ktor’s Application scope is one example, while Android viewModelScope uses a SupervisorJob without necessarily supplying a CoroutineExceptionHandler. section 18.6 section 18.3
