Exception & Cancellation Handling in Coroutines

📖 Concept

Exception handling in coroutines is one of the most misunderstood topics. The rules are different from regular try-catch, and getting them wrong leads to silent failures or crashes.

Key rules:

  1. Exceptions in launch propagate to the parent — crash if unhandled
  2. Exceptions in async are stored and rethrown on .await()
  3. CancellationException is NEVER propagated — it's used for normal cancellation
  4. CoroutineExceptionHandler catches exceptions from launch only (not async)
  5. In supervisorScope, child failures don't propagate to siblings

Exception propagation:

launch {                    // Exception propagates UP immediately
    throw RuntimeException() // → parent Job cancelled → ALL siblings cancelled
}

async {
    throw RuntimeException() // Exception stored until await() is called
}

The CancellationException trap: CancellationException is a special exception used for coroutine cancellation. If you catch it, you break structured concurrency — the coroutine won't cancel properly. ALWAYS rethrow it.

CoroutineExceptionHandler: Only works on top-level coroutines (root coroutines started with launch). Does NOT work with async or nested coroutines.

💻 Code Example

codeTap to expand ⛶
1// Exception handling patterns
2
3// Pattern 1: try-catch in the coroutine body
4viewModelScope.launch {
5 try {
6 val data = repository.fetchData() // throws IOException
7 _state.value = UiState.Success(data)
8 } catch (e: CancellationException) {
9 throw e // ALWAYS rethrow!
10 } catch (e: Exception) {
11 _state.value = UiState.Error(e.message ?: "Unknown error")
12 }
13}
14
15// Pattern 2: CoroutineExceptionHandler for top-level launch
16val handler = CoroutineExceptionHandler { _, exception ->
17 Log.e("Coroutine", "Unhandled: ${exception.message}")
18 _state.value = UiState.Error(exception.message ?: "")
19}
20viewModelScope.launch(handler) {
21 repository.fetchData() // If this throws, handler catches it
22}
23
24// Pattern 3: Result type for clean error handling
25sealed interface DataResult<out T> {
26 data class Success<T>(val data: T) : DataResult<T>
27 data class Error(val exception: Throwable) : DataResult<Nothing>
28}
29
30suspend fun <T> safeApiCall(block: suspend () -> T): DataResult<T> {
31 return try {
32 DataResult.Success(block())
33 } catch (e: CancellationException) {
34 throw e
35 } catch (e: Exception) {
36 DataResult.Error(e)
37 }
38}
39
40// Usage
41viewModelScope.launch {
42 when (val result = safeApiCall { api.getUser(id) }) {
43 is DataResult.Success -> _state.value = UiState.Loaded(result.data)
44 is DataResult.Error -> _state.value = UiState.Failed(result.exception.message)
45 }
46}
47
48// SupervisorScope for independent operations
49viewModelScope.launch {
50 supervisorScope {
51 // Each child handles its own errors
52 val feed = async {
53 try { api.getFeed() } catch (e: Exception) { emptyList() }
54 }
55 val profile = async {
56 try { api.getProfile() } catch (e: Exception) { null }
57 }
58 _state.value = HomeState(feed.await(), profile.await())
59 }
60}

🏋️ Practice Exercise

Practice:

  1. Explain why catching CancellationException breaks structured concurrency
  2. Implement a safeApiCall wrapper that handles all exceptions properly
  3. What happens if an exception is thrown inside an async block but await() is never called?
  4. Design an error handling strategy for a ViewModel with 5 concurrent API calls
  5. Explain the difference between try-catch and CoroutineExceptionHandler

⚠️ Common Mistakes

  • Catching all exceptions including CancellationException — breaks cancellation. Use catch(e: Exception) and rethrow CancellationException.

  • Using CoroutineExceptionHandler with async — it doesn't work, exceptions are only rethrown on await()

  • Not handling exceptions in supervisorScope children — they fail silently without try-catch

  • Assuming try-catch around launch catches exceptions — it doesn't, the exception propagates to the parent Job

💼 Interview Questions

🎤 Mock Interview

Mock interview is powered by AI for Exception & Cancellation Handling in Coroutines. Login to unlock this feature.