catch

fun <T> Flow<T>.catch(action: suspend FlowCollector<T>.(cause: Throwable) -> Unit): Flow<T>(source)

Catches exceptions in the flow completion and calls a specified action with the caught exception. This operator is transparent to exceptions that occur in downstream flow and does not catch exceptions that are thrown to cancel the flow.

For example:

flow { emitData() }
.map { computeOne(it) }
.catch { ... } // catches exceptions in emitData and computeOne
.map { computeTwo(it) }
.collect { process(it) } // throws exceptions from process and computeTwo

Conceptually, the action of catch operator is similar to wrapping the code of upstream flows with try { ... } catch (e: Throwable) { action(e) }.

Any exception in the action code itself proceeds downstream where it can be caught by further catch operators if needed. If a particular exception does not need to be caught it can be rethrown from the action of catch operator. For example:

flow.catch { e ->
if (e !is IOException) throw e // rethrow all but IOException
// e is IOException here
...
}

The action code has FlowCollector as a receiver and can emit values downstream. For example, caught exception can be replaced with some wrapper value for errors:

flow.catch { e -> emit(ErrorWrapperValue(e)) }

The action can also use emitAll to fallback on some other flow in case of an error. However, to retry an original flow use retryWhen operator that can retry the flow multiple times without introducing ever-growing stack of suspending calls.