All topics
RxJSintermediate

Error Handling with catchError

Explain how catchError intercepts an Observable's error and how to recover, rethrow, or substitute a fallback stream.

When an Observable errors, it terminates — no further next emissions will ever occur on that stream, even if the underlying source could theoretically keep producing values, which is exactly why unhandled errors in RxJS pipelines are so disruptive: a single failed HTTP request can silently kill an entire subscription that was expected to keep emitting indefinitely (like a polling interval), unless error handling is deliberately put in place.

It's like a safety net under a trapeze act — if a performer falls (an error occurs), the net catches them and the show can continue with a substitute act (a fallback value) rather than the entire performance ending abruptly the moment anyone falls.

Key Concepts

1
catchError is the standard operator for intercepting an error before it propagates further and deciding what happens next: it can return a fallback Observable (commonly of(defaultValue) to substitute a safe default and let the stream continue as if nothing failed from the subscriber's point of view), or it can rethrow (return throwError(() => err)) to let the error continue propagating if the calling code needs to react to the failure itself, such as showing an error toast.
catchErrorof(defaultValue)throwError(() => err)
2
A common interview gotcha is placement: catchError only catches errors from the Observable chain upstream of where it's placed in the .pipe() call — if a catchError is meant to protect a retryable operation and substitute a fallback so a parent stream (like a polling interval) keeps running, it generally needs to be placed inside the inner Observable of a flattening operator (like inside the function passed to switchMap), not on the outer stream, since an error on the outer stream terminates it regardless of any downstream catchError.
catchError.pipe()intervalswitchMap
3
A well-rounded answer also contrasts catchError with simply passing an error callback to .subscribe(): the subscribe-level error handler is the last line of defense and can't allow the stream to continue or recover — by the time an error reaches it, the Observable is already terminated, whereas catchError inside the pipe can genuinely intercept and recover from the error before it reaches that point.
catchErrorerror.subscribe()subscribe