xstate: invoke.onError transition not taken when error is thrown
Description
When using invoke with an onError transition I expected throwing an error (or rejecting a promise) from the invoked thing to trigger the onError transition.
Expected Result
onError gets invoked, no error is reported to the console, and I decide what to do with the thrown error.
Actual Result
Console shows a red error and the onError transition is never taken.
Reproduction
https://codesandbox.io/s/xstate-child-machine-errors-h5csv
Additional context
I asked this question in gitter and @davidkpiano suggested that this should work correctly. @amitnovick had a useful workaround where you can sendParent(error(<invoked-id>, data)) which works but feels very clunky and requires child machines to know the id they were invoked with which is problematic.
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 3
- Comments: 37 (32 by maintainers)
With the upcoming XState 4.7, your example will look like:
@vivek12345 Can you open a separate issue for that enhancement request?
Also please keep in mind that
escalate(...)and all other action creators are action creators. They do not actually execute the action.D’oh! 🤦
It could still work using Guards though:
Note: implementing this would be a breaking change, since
onErrorwould be called in cases where it currently doesn’t.I think that for invoked machines it just won’t be called (right now) at all - so this leaves you with only one error handler in the parent 😉
The feedback received here is great ❤️ we’d really like to recognize the needs of the community to design the API with that in mind. This means that there probably won’t be any “quick actions” taken here (maybe besides adjusting the docs), but be sure that we are going to improve the story (one way or another) around this.
This is definitely a big part of it, though I’m actually less annoyed by the need to explicitly pass errors up to the parent than I am by having two separate paths in the parent to handle different types of errors.
In my mind
onErrormeans “the child errored and I want to handle it”, which is currently only true for some of the errors in the child unless you use thesendParent(error())workaround.To be clear, I’m not mad that we disagree on this. We just have very opposing opinions on this and that’s totally fine!
I’ve got a solution that works even if it’s a little awkward. It’s at least less awkward than forcing every
invokestate with a machine that can die to handleonErroridentically in two different places.Keep also in mind - that I’m just discussing and stating personal opinions. I’m not a native english speaker and my writing might suffer from that - might sound a little bit harsh, too confident etc but that is certainly not my intention.
Just to add a little bit to the discussion - I believe that even if u need to introduce some duplication it might be good because it forces you to be explicit which is really helpful for future readers. It also kinda makes the possible visualization better because the intent being visible closer to the failing call site (so on the same diagram, rather than somewhere else).
I wouldn’t also recommend “hacking” the system by using things like
sendParent(error(<child-id>))- I would recommend creating a dedicated event for a particular failure. I believe that onlyerror.execution(runtime errors caused by typos, undefined access etc) should really be handled as “errors” - those are unexpected (and can only be avoided with proper testing). Everything else is not that much of an error - it’s just something that happens in the system and is expected to happen.