What is a SupervisorJob in Kotlin coroutines, and how does it differ from a regular Job when one child fails?
answer
- Regular Job: child fails -> parent + all siblings cancelled
- SupervisorJob: child fails -> siblings + parent survive
- Cancellation still flows DOWN with supervisor
- supervisorScope { } = scope backed by SupervisorJob
- Does not swallow exceptions; still need a handler
basics
~10 sA SupervisorJob is a special parent for coroutines. If one child crashes, the others keep running and the parent stays alive. With a regular Job, one crash cancels everything.
solid answer
~40 sA Job is the handle that controls a coroutine's lifecycle and links parents to children. With a normal Job, failure cancellation propagates both ways: a failing child cancels its parent, which then cancels all sibling children. SupervisorJob() reverses the downward part: a child's failure is NOT propagated upward, so the parent and siblings keep running; cancellation still flows downward (cancelling the parent cancels all children). You install it via CoroutineScope(SupervisorJob() + Dispatchers.Default), or use the supervisorScope { } builder which creates a scope backed by a SupervisorJob for its block. This makes it the basis for resilient scopes where independent tasks must not take each other down — e.g. a UI scope handling several independent requests.
code
kotlin · 3 linesval scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
scope.launch { throw RuntimeException("boom") } // dies alone
scope.launch { delay(100); println("still alive") } // survivesgo deeper
Can state that a failing child does not cancel siblings or parent, unlike a regular Job.
Adds that cancellation still flows downward and knows supervisorScope as the structured form.
Explains that exceptions are still reported and require a handler; distinguishes propagation from handling.
Frames it as the building block for resilient scopes and reasons about when downward-only failure isolation is the right contract.
## What a Job is Every coroutine has a `Job` — an object that represents its lifecycle (active, completing, cancelled, completed) and forms a **parent/child tree**. A coroutine launched inside a scope becomes a child of that scope's Job. This tree is what powers **structured concurrency**: parents wait for children, and cancellation flows through the tree. ## Normal Job: failure propagates BOTH ways With a regular `Job`, when a child coroutine **fails** (throws an uncaught exception, not a `CancellationException`): 1. The exception propagates **up** to the parent. 2. The parent cancels itself. 3. The parent then cancels **all its other children** (the failing child's siblings). So one crash takes down the whole scope. This is correct when the tasks are interdependent. ## SupervisorJob: failure does NOT propagate up `SupervisorJob()` is a `Job` whose children **fail independently**. A child's failure is not sent to the parent, so the parent and the siblings survive. Crucially, **cancellation still flows downward**: cancelling the SupervisorJob (or the scope) cancels all its children. ```kotlin import kotlinx.coroutines.* fun main() = runBlocking { val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default) val a = scope.launch { delay(50) throw RuntimeException("A failed") } val b = scope.launch { delay(200) println("B finished fine") // still runs } joinAll(a, b) } ``` With a plain `Job()` instead of `SupervisorJob()`, the failure of `a` would cancel `b`. ## How you use it - **`CoroutineScope(SupervisorJob() + dispatcher)`** — a long-lived scope (e.g. a service or ViewModel) where independent jobs must not kill each other. - **`supervisorScope { }`** — a suspending builder that creates a child scope backed by a SupervisorJob for the duration of the block, then waits for all children. This is the structured way to get supervisor behavior inside a function. ## Important caveat: exceptions still need handling A SupervisorJob does not *swallow* exceptions. An unhandled child exception is delivered to a `CoroutineExceptionHandler` (for `launch`) or re-thrown on `await()` (for `async`). If no handler is installed, it reaches the thread's uncaught-exception handler. SupervisorJob only changes **propagation among siblings**, not whether the exception is reported.
- Does using a SupervisorJob mean child exceptions can be ignored?No. It only stops failure from cancelling siblings/parent. The exception is still reported — to a CoroutineExceptionHandler for launch, or on await() for async — so you must still handle it.
A regular Job is a string of fairy lights wired in series — one dead bulb kills the strand; a SupervisorJob wires them in parallel — one bulb dies, the rest stay lit.
saying these in an interview costs you the question
- Saying SupervisorJob makes exceptions disappear / get swallowed
- Claiming cancellation no longer flows downward
- Thinking it changes async's await() rethrow behavior
- Confusing it with try/catch error handling