How to add a global error handler for all redux-observable epics?

2020 update

import { concat, of } from 'rxjs';
import { combineEpics } from 'redux-observable';
import { catchError } from 'rxjs/operators';

export const rootEpic = (action$, store) =>
  combineEpics(epic1, epic2, epic3)(action$, store)
    .pipe((error, caught) => concat(caught));
import { createEpicMiddleware } from 'redux-observable';
const epicMiddleware = createEpicMiddleware();
epicMiddleware.run((action$, store) => rootEpics(action$, store));

You can additionally pass redux error action like that:

concat(of(reduxActionForError), caught));

redux-observable leans on idiomatic paradigms of RxJS for nearly everything you would do. When you combine Epics together, you're creating a new Epic that has the normal function (action$, store) signature. So this new Epic represents everything output from those individual Epics, including errors.

You can leverage that fact by defining your own rootEpic and composing the result of your combineEpics() call. Since Epics are factories, you'll need to remember to pass along (action$, store)!

Here's one way:

export const rootEpic = (action$, store) =>
  combineEpics(epic1, epic2, epic3)(action$, store) // <-- call epic w/ args
    .do({ error: err => console.error(err) });

You probably shouldn't use the .catch() operator for this, because .catch() is for returning some other Observable to use instead, when there is an error. If you just want to log the error out (and not try to recover), .do() is the ideal operator--its purpose is to allow you to do external side effects (like logging) that have no effect on the stream itself.

Composing Epics is very powerful, not just in this case, but many others that we're planning to describe soon.

Related to the .catch() operator, you may also be interested in the RFC for better default error handling.