Inside a `catch { }` block, what are your options for the caught throwable, and how do you selectively handle only certain exception types?
answer
- Swallow / recover (emit) / propagate (throw)
- Receiver is FlowCollector → emit available
- Branch on type, rethrow the rest
- NEVER swallow CancellationException
- Translate to domain exceptions for clean APIs
basics
~10 sInside catch you can ignore the error, log it, send a backup value with emit, or rethrow it. To handle only some error types, check the type and rethrow the rest.
solid answer
~40 sInside `catch { e -> }` the throwable is bound (commonly as `it`). Options: (1) swallow — do nothing, flow completes normally; (2) recover — call `emit`/`emitAll` to provide fallback values; (3) propagate — `throw it` or throw a translated exception. Because `catch` catches *all* upstream `Throwable`s, selective handling is done manually: branch on the type and rethrow what you don't own, e.g. `if (it is IOException) emit(fallback) else throw it`. Be careful never to swallow `CancellationException` — `catch` will receive it if the upstream was cancelled, and silently handling it breaks structured concurrency. Idiomatically, rethrow cancellation: many codebases write `if (it is CancellationException) throw it`. Use translated domain exceptions to keep the contract clean for callers.
code
kotlin · 9 linesuserFlow
.catch { e ->
if (e is CancellationException) throw e
when (e) {
is IOException -> emit(User.GUEST) // recover
else -> throw DomainError("load failed", e) // translate + propagate
}
}
.collect(::show)go deeper
Knows you can log or emit a fallback inside catch.
Knows the swallow/recover/propagate options and basic type branching.
Explicitly guards CancellationException and translates exceptions for a clean contract.
Defines a team convention (rethrow cancellation, domain translation, recover only known-safe types) and reasons about partial-failure semantics.
## The lambda's receiver and parameter `catch(action: suspend FlowCollector<T>.(Throwable) -> Unit)`. So inside the block: - the receiver is a `FlowCollector<T>` → `emit` / `emitAll` are available; - the parameter is the caught `Throwable` (default name `it`). ## Three things you can do 1. **Swallow** — log/ignore; the flow then completes successfully (no more emissions). 2. **Recover** — `emit(default)` or `emitAll(fallbackFlow)`. 3. **Propagate** — `throw it` (re-raise) or `throw DomainException(it)` (translate). ## Selective handling by type `catch` is type-agnostic — it intercepts every upstream `Throwable`. To handle only some, branch and rethrow the rest: ```kotlin repository.stream() .catch { e -> when (e) { is IOException -> emit(cachedFallback) // recover network errors else -> throw e // let everything else propagate } } .collect(::render) ``` ## Watch out: CancellationException If the collecting coroutine is cancelled, a `CancellationException` may surface as an upstream throwable in `catch`. **Swallowing it breaks structured concurrency** — the coroutine would appear to finish normally instead of being cancelled. Always rethrow it: ```kotlin .catch { e -> if (e is CancellationException) throw e emit(fallback) } ``` (Modern coroutines try to make cancellation propagate, but explicit rethrow is the safe, idiomatic guard, mirroring the general rule for catching `Throwable` in coroutine code.) ## Translating exceptions For clean APIs, convert low-level failures into domain types so callers don't depend on transport details: ```kotlin .catch { throw DataUnavailableException(cause = it) } ``` ## Key APIs / keywords - `catch { }`, `emit`, `emitAll`, `throw`. - `CancellationException` — must be rethrown. - `is` type checks / `when` for selective handling.
- Why must you rethrow `CancellationException` from inside `catch`?Because it signals structured-concurrency cancellation; swallowing it makes a cancelled coroutine look like it completed normally, leaking work and breaking parent/child cancellation.
- How do you handle only `IOException` and propagate everything else?Type-check inside the block: `if (it is IOException) emit(fallback) else throw it`.
saying these in an interview costs you the question
- Swallowing all throwables including CancellationException
- Assuming `catch` only catches the type you care about
- Not knowing you can emit/emitAll inside catch
- Leaking transport exceptions to callers instead of translating