CoroutineExceptionHandler

Last-resort Handler

A CoroutineExceptionHandler is called for exceptions that nobody caught. It works only on a root coroutine (launched directly in a scope) or a direct child of a supervisor. It is the place to log the crash and show a generic error. It cannot "recover" the coroutine: it is already finished.

Example

import kotlinx.coroutines.*

fun main() = runBlocking {
    val handler = CoroutineExceptionHandler { context, e ->
        val name = context[CoroutineName]?.name
        println("Crash in '$name': ${e::class.simpleName}: ${e.message}")
    }
    val appScope = CoroutineScope(SupervisorJob() + handler)

    val job = appScope.launch(CoroutineName("syncContacts")) {
        delay(50)
        throw IllegalStateException("contacts permission missing")
    }
    job.join()

    appScope.launch(CoroutineName("syncPhotos")) {
        println("Scope still works after a failure")
    }.join()
    appScope.cancel()
}

Output

Crash in 'syncContacts': IllegalStateException: contacts permission missing
Scope still works after a failure

Where Handlers Are Ignored

  • On a nested child of a normal Job: the exception goes to the parent first, the child handler is ignored.
  • On async: the exception is kept for await().
  • For CancellationException: it is a normal cancel, not an error.

Tip: On Android, install one handler in your app-level scope that logs to Crashlytics, and still catch expected errors (network, validation) close to where they happen.