SupervisorJob is a Job for a coroutine scope whose children may fail independently. With a regular Job, an uncaught child exception fails the parent, cancels sibling coroutines, and continues up the coroutine hierarchy. A SupervisorJob keeps the parent and its other children running instead. Use it when concurrent tasks share a lifecycle but not a single all-or-nothing result, such as long-running application components or UI work. section 18.2 section 18.2.3
Attach a supervised scope to a component
Create a standalone scope with a dispatcher plus SupervisorJob, launch work through that scope, and cancel the scope when the component stops. Cancellation still reaches every child; supervision changes failure propagation, not lifecycle ownership. private val scope = CoroutineScope(dispatcher + SupervisorJob()) fun start() { scope.launch { while (true) { delay(500.milliseconds); log("Working") } } scope.launch { log("One-off task"); delay(500.milliseconds) } } fun stop() = scope.cancel() section 15.1.2
This is deliberately different from using a regular scope for concurrent decomposition. When several children cooperate to calculate one common result, cancelling the remaining work after one failure saves resources and reflects that the result can no longer be produced. A supervisor fits independent work: one failed fetch or background task should not terminate a server process, UI component, or unrelated sibling task. section 18.2.1 section 18.2.3
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.
Compare the failure paths visually: under supervision, a child failure leaves the parent and sibling coroutines running instead of propagating the exception through the hierarchy. section 18.2.3
A SupervisorJob remains part of structured concurrency. Cancelling its scope still cancels its children, and cancellation exceptions continue to propagate correctly. Supervision therefore isolates ordinary child failures without creating detached, unmanaged coroutines. section 18.2.3 section 15.3
Handle failures explicitly rather than treating supervision as silent recovery:
- A direct child started with launch can report an uncaught exception to the CoroutineExceptionHandler, even while siblings continue running. section 18.2.3 section 18.3
- A top-level async coroutine exposes its failure through Deferred; the consumer handles it by calling await() and using try-catch as needed. section 18.3.1
- Provide a CoroutineExceptionHandler when uncaught launch failures need custom logging or reporting; it is a last-resort handler, not a replacement for local recovery. section 18.3 section 18.6
Add reporting to a supervised scope
Place SupervisorJob, a dispatcher, and a CoroutineExceptionHandler in the same context. A failing launch child is reported while the scope can continue serving other work. private val handler = CoroutineExceptionHandler { _, e -> println("[ERROR] ${e.message}") } private val scope = CoroutineScope(SupervisorJob() + dispatcher + handler) fun action() = scope.launch { throw UnsupportedOperationException("Ouch!") } section 18.3
