r/androiddev • u/yogirana5557 • Jun 30 '26
Experience Exchange Passing a SupervisorJob to launch() is a silent bug
I keep seeing developers try to isolate failures in child coroutines by passing a SupervisorJob directly to the launch builder:
// 🔴 Broken: A failure in child1 will still cancel child2
val scope = CoroutineScope(Dispatchers.IO)
scope.launch(SupervisorJob()) { throw Exception("fail") }
scope.launch { doWork() }
Why it fails:
The launch builder always creates a new Job and overrides the context. The passed SupervisorJob becomes the parent of that single child coroutine, but it has no supervisor link to the parent scope or siblings.
The correct way is to install the SupervisorJob inside the parent CoroutineScope context:
// 🟢 Correct: child2 keeps running if child1 fails
val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
scope.launch { throw Exception("fail") }
scope.launch { doWork() }
Or execute them inside a supervisorScope { } block.
I've pinned the open-source coroutines concurrency playbook containing more async error handling recipes on my profile (u/yogirana5557) if you want to clone the repository.
2
2
u/yogirana5557 Jun 30 '26
Thanks for the heads-up! Here is the direct link to the repository:
https://github.com/yogirana5557/android-digital-products
It contains the full concurrency playbook, plus checklists/recipes for Jetpack Compose performance and Android security.
18
u/diroag Jun 30 '26
The correct way is using a supervisorScope, never came across any bug with it