7

I'd like to handle errors in NGXS in two ways. The first way is handling an error on the store dispatch in a component. The second way is a global angular error handler as a fallback for when the error is unhandled (not handled by first way).

But the problem is that the global error handler is always called by NGRX when there is an error in an action handler.

So given this code in the component:

this.store.dispatch(new FetchAction())
   .subscribe( error: () => console.log('fetch error occured'))

this action handler:

fetch() {
   return this.http.get('..');
}

and this global error handler:

export class GlobalErrorHandler extends ErrorHandler {
   handleError(err: any) {
      console.log('unexpected error occured');
   }
}

There would be two error messages in the console. One from the subscribe of the dispatch and one from the global error handler because there was an error in the action handler.

Now I could catch errors in the action handler but that would be the wrong place to do so because the action handler shouldn't know that an error from this action is handled in a component.

I made a stackblitz which shows two errors in the console when clicking the button: https://stackblitz.com/edit/ngxs-error-throw-r42fkb?file=src/app/app.component.ts

cmart
  • 991
  • 3
  • 11
  • 19
  • 1
    Hi, I recommend you to learn about Angular Interceptors: https://scotch.io/@vigneshsithirai/angular-6-7-http-client-interceptor-with-error-handling#toc-creating-angular-interceptor –  Jul 12 '19 at 17:58
  • I do know about http interceptors but it's not a solution for this. A http interceptor still has to return a result. You can't obviously catch an error there and tell the app the response was a success and return it some value (what would that even be?) when indeed it wasn't successful at all. It only works when having no global error handler. But having a global error handler should be supported all the way because it's an integral part of angular – cmart Nov 19 '19 at 17:19
  • Created a new stackblitz with reproduction of that issue: https://stackblitz.com/edit/ngxs-global-error-handler-fails-to-catch-async-errors-in-action Hope somebody will help. – oliverfrost21 Jan 29 '20 at 14:48
  • Did you manage to find a solution to this one? I have the same problem. – fikkatra Sep 29 '21 at 11:30

2 Answers2

1

Use RxJS catchError operator to handle errors at low level:

This way, you caught the error so it won't call your global action handler.

this.store.dispatch(new FetchAction())
   .pipe(catchError(error => /* Do something specific with the error. */))
   .subscribe(() => { /* Do something with the success result. */ })

Let the error bubble up to the global action handler:

Nothing special here. Just ignore the error. Do this when you don't know what to do with the error.

this.store.dispatch(new FetchAction())
   .subscribe(() => { /* Do something with the success result. */ })

You can re-throw a more specific error if you want with throwError so you have more information when you will handle it at a higher level.

this.store.dispatch(new FetchAction())
   .pipe(catchError(error => throwError(new Error('Some specific error.'))))
   .subscribe(() => { /* Do something with the success result. */ })
Maxime Gélinas
  • 2,202
  • 2
  • 18
  • 35
  • 2
    "This way, you caught the error so it won't call your global action handler." I guess you mean global error handler. But that is exactly the point. This will STILL call the global error handler when there is an error in an ngxs action handler. If you don't have the catchError and just subscribe the global error handler will actually be called twice. – cmart Nov 19 '19 at 17:11
  • I don't know what is causing this, maybe you can use a `Subject` type piped with `distinctUntilChanged` or `debounceTime` inside your custom global error handler to ignore duplicates. – Maxime Gélinas Nov 20 '19 at 00:10
  • 1
    Yes, that's what I'm using but obviously this is not ideal. Also it does not solve the problem. When I dispatch an action, subscribe to it and use catchError I'd think I'd be able to explicitly handle this error. But the global error handler is still called. So it's not possible to have the global error handler as a fallback that is only called when I don't handle errors on dispatch – cmart Nov 20 '19 at 07:37
0

I have the same problem. But as the documentation says it is by design. Error is beging propagated to main ErrorHandler despite you do handle on the upper level.

If an unhandled exception is thrown inside an action, the error will be propagated to the ErrorHandler and you can also catch it subscribing to the dispatch Observable. If you subscribe to the dispatch Observable the error will be caught twice, once in the ErrorHandler and on your dispatch handle

jars
  • 49
  • 4
  • 11