SupervisorJob and Error Isolation
Isolate coroutine failures with SupervisorJob: independent child coroutines, partial failure handling.
import kotlinx.coroutines.*
// Without supervisor — one failure cancels siblings
suspend fun withoutSupervisor() {
println("--- Without Supervisor ---")
try {
coroutineScope {
launch {
delay(100)
println(" Child 1 started")
delay(500)
println(" Child 1 completed") // Won't reach
}
launch {
delay(200)
println(" Child 2 about to fail")
throw RuntimeException("Child 2 failed!")
}
launch {
delay(100)
println(" Child 3 started")
delay(500)
println(" Child 3 completed") // Won't reach
}
}
} catch (e: RuntimeException) {
println(" Caught: ${e.message}")
println(" All children were cancelled!")
}
}
// With supervisor — failures are isolated
suspend fun withSupervisor() {
println("\n--- With Supervisor ---")
supervisorScope {
val child1 = launch {
delay(100)
println(" Child 1 started")
delay(500)
println(" Child 1 completed ✓")
}
val child2 = launch {
delay(200)
println(" Child 2 about to fail")
throw RuntimeException("Child 2 failed!")
}
val child3 = launch {
delay(100)
println(" Child 3 started")
delay(500)
println(" Child 3 completed ✓")
}
// Handle child2's failure
child2.invokeOnCompletion { cause ->
if (cause != null) println(" Child 2 failed: ${cause.message}")
}
// Wait for all
listOf(child1, child3).forEach { it.join() }
}
}
// Practical: parallel data fetching with partial results
data class FetchResult<T>(
val source: String,
val data: T? = null,
val error: String? = null
) {
val isSuccess get() = data != null
}
suspend fun <T> safeFetch(
source: String,
block: suspend () -> T
): FetchResult<T> = try {
FetchResult(source, data = block())
} catch (e: Exception) {
FetchResult(source, error = e.message)
}
suspend fun fetchAllData() = supervisorScope {
println("\n--- Partial Fetch ---")
val results = listOf(
async { safeFetch("users") { delay(100); listOf("Alice", "Bob") } },
async { safeFetch("orders") { delay(50); throw RuntimeException("DB timeout") } },
async { safeFetch("products") { delay(150); listOf("Laptop", "Mouse") } },
async { safeFetch("metrics") { delay(200); mapOf("views" to 1000) } }
).awaitAll()
val (successes, failures) = results.partition { it.isSuccess }
println(" Successes: ${successes.map { "${it.source}: ${it.data}" }}")
println(" Failures: ${failures.map { "${it.source}: ${it.error}" }}")
results
}
// SupervisorJob in a CoroutineScope
class TaskRunner {
private val scope = CoroutineScope(
SupervisorJob() + Dispatchers.Default + CoroutineExceptionHandler { _, e ->
println(" [Handler] Uncaught: ${e.message}")
}
)
fun runTask(name: String, block: suspend () -> Unit): Job =
scope.launch {
println(" Task '$name' starting")
block()
println(" Task '$name' completed")
}
fun shutdown() {
scope.cancel()
println(" TaskRunner shut down")
}
}
fun main() = runBlocking {
withoutSupervisor()
withSupervisor()
fetchAllData()
// TaskRunner with SupervisorJob
println("\n--- TaskRunner ---")
val runner = TaskRunner()
val t1 = runner.runTask("A") { delay(200) }
val t2 = runner.runTask("B") { delay(100); throw RuntimeException("B failed") }
val t3 = runner.runTask("C") { delay(300) }
delay(500)
runner.shutdown()
}Use Cases
- Independent failure isolation in microservices
- Partial result aggregation from multiple sources
- Resilient background task management
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
Coroutine Exception Handling and Supervision
Handle errors in coroutines: CoroutineExceptionHandler, supervisorScope, try-catch, and error propagation.
Best for: Robust error handling in concurrent operations
Coroutines — launch, async, and Structured Concurrency
Write concurrent code with Kotlin coroutines: launch, async/await, structured concurrency, and dispatchers.
Best for: Parallel API calls in backend services
Flow — Reactive Streams with Coroutines
Build reactive pipelines with Kotlin Flow: emit, collect, transform, combine, and error handling.
Best for: Streaming data processing pipelines
Channels — Producer-Consumer with Coroutines
Communicate between coroutines with Channels: produce, actor pattern, fan-out/fan-in, and select.
Best for: Producer-consumer patterns in coroutines