Get started

catch

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

If the upstream flow completes with an exception, this operator catches that exception and calls the specified action with it. 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) }.

After the flow has completed with an exception, it cannot emit any more values, and fallback values may be emitted downstream from the action block instead.

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.