When should you use the `catch { }` operator versus wrapping the `collect` call in a plain `try/catch`? What does each cover?
answer
- catch = upstream only; try/catch around collect = everything
- catch can emit fallbacks; outer try cannot
- collector-body errors need the outer try/catch
- Combine: catch upstream + try/catch terminal
- Rethrow CancellationException in the outer catch
basics
~10 sUse catch for errors coming from the flow itself (upstream). Use a try/catch around collect when you also need to handle errors from your collector code. They cover different parts.
solid answer
~40 s`catch { }` only intercepts **upstream** exceptions (builder + operators above it) and never the collector body — that's exception transparency. A `try/catch` placed *around the whole `collect` call* covers everything the collection raises: upstream errors that reached the terminal operator AND exceptions thrown inside the collector lambda. So they're complementary. Prefer `catch` when you want to recover or emit fallbacks while keeping the stream going and keep producer-error handling declarative and transparency-safe. Use an outer `try/catch` around `collect` when you must also handle collector-side failures (e.g. rendering throws) or want one boundary for the whole consumption. A frequent idiom: `catch` to recover upstream + an outer try/catch (or `runCatching`) around `collect` to handle terminal/collector failures separately.
code
kotlin · 8 linestry {
source
.catch { emit(Default) } // upstream recovery, transparency-safe
.collect { sink.write(it) } // write() failure -> outer catch
} catch (e: Throwable) {
if (e is CancellationException) throw e
log.error("consume failed", e)
}go deeper
Knows catch is for flow errors and try/catch wraps the collect call.
States the upstream vs collector-body coverage difference clearly.
Combines both with correct separation of concerns and cancellation handling.
Establishes a consumption-boundary convention across the codebase and reasons about where recovery vs failure surfaces belong.
## Two different scopes | Mechanism | Catches upstream errors? | Catches collector-body errors? | Can emit fallbacks? | |---|---|---|---| | `catch { }` operator | Yes (above it only) | **No** | Yes (`emit`/`emitAll`) | | `try/catch` around `collect` | Yes (they surface at the terminal) | **Yes** | No (you're outside the flow) | ## Why the difference exists `catch` is upstream-only by design (exception transparency). The collector body's exceptions are downstream, so `catch` can't see them. But once they propagate out of `collect`, an enclosing `try/catch` can. ```kotlin // catch: upstream recovery, transparency-safe upstream .catch { emit(fallback) } // handles producer failure .collect { render(it) } // a throw HERE is not caught by catch ``` ```kotlin // try/catch around collect: covers collector body too try { upstream.collect { render(it) } // render() may throw } catch (e: Throwable) { if (e is CancellationException) throw e showError(e) } ``` ## Combine them Use both for clear separation of concerns: ```kotlin try { repo.stream() .catch { emit(emptyList()) } // recover producer errors, keep streaming .collect { ui.render(it) } // collector failures fall through to the try } catch (e: Throwable) { if (e is CancellationException) throw e ui.showFatal(e) } ``` ## Decision guide - Need a **fallback value** and keep emitting → `catch`. - Need to handle **collector/render** failures → outer try/catch. - Want declarative, transparency-safe producer handling → `catch`. - Want a single consumption boundary including downstream → try/catch around `collect`. ## Key APIs / keywords - `catch { }` (upstream), `emit`, `emitAll`. - terminal `collect`; `try/catch`; `runCatching` as a variant. - Remember CancellationException rethrow in the outer catch too.
- Can a `try/catch` around `collect` emit a fallback into the flow?No — by then you're outside the flow's collector context; there's no `FlowCollector` to emit into. Only `catch { }` can emit fallbacks.
- If the collector throws, will an upstream `catch` ever see it?No. Exception transparency keeps downstream (collector) exceptions out of `catch`; they only surface to an enclosing try/catch around `collect`.
saying these in an interview costs you the question
- Expecting `catch` to handle collector-body exceptions
- Believing try/catch around collect can emit fallbacks into the flow
- Using only one mechanism when both concerns exist
- Forgetting CancellationException in the outer catch