Xstate: Issue with onError in parallel states

Created on 17 Apr 2019  路  1Comment  路  Source: davidkpiano/xstate

Bug or feature request?

Given a parallel state whose concurrent substates invoke some services, it appears that when you reject() a promise in order to fire onError in one substate, that XState is firing onError for ALL of the substates.

Description:

I have a parallel state with three concurrent substates (Q1, Q2, Q3). Each state attempts to pop() an element off of an array.

If the pop() is successful, the promise will resolve() and it will call the service's onDone. onDone just transitions to itself so the substate can repeat popping elements over and over.

When the array is empty, however, the pop() will fail and it should call onError. When onError is detected, it should transition to a 'final' state and wait there until the other Q's have also entered their 'final' state.

(Bug) Expected result:

When ALL substates hit the 'final' state then the parallel state's onDone should fire as expected.

(Bug) Actual result:

The parallel state's onDone is being triggered after only ONE substate has called onError.

That tells me that onError is causing the other substates' onError to be fired as well.

(Bug) Potential fix:

If it is intended that onError from one substate should cascade to other substates, then there is no sane way to implement retry logic. onError should be handled by only the substate that fired it, and leave it to that substate to communicate an error that should cascade.

Link to reproduction or proof-of-concept:

The behavior is reproduced in this sandbox: https://codesandbox.io/s/1owjq73763

All of the code is in src/components/HelloWorld.vue

The console demonstrates:
1) When the first Q rejects() based on popping an empty array and calls onError,
2) ALL of the other substates' onError trigger, and
3) cause ALL substates to go to their 'final' state, which
4) forces the final onDone to be called before the other substates are actually done.

We should not see "finish" before we see any "fetchQueue:Qn:slept=x".

bug

Most helpful comment

I've tried to debug this a little bit, at this point I'm not yet able to fix the issue at hand - but those are my findings:

  • error event holds not enough contextual metadata which would allow states to identify it as "its own", so onError on each parallel state gets invoked
  • this means that basically any state that is ready to receive error event at the moment will get its onError called, even across machines (with forwarded events) - https://codesandbox.io/s/yk419mqqlz

>All comments

I've tried to debug this a little bit, at this point I'm not yet able to fix the issue at hand - but those are my findings:

  • error event holds not enough contextual metadata which would allow states to identify it as "its own", so onError on each parallel state gets invoked
  • this means that basically any state that is ready to receive error event at the moment will get its onError called, even across machines (with forwarded events) - https://codesandbox.io/s/yk419mqqlz
Was this page helpful?
0 / 5 - 0 ratings

Related issues

mattiamanzati picture mattiamanzati  路  3Comments

pke picture pke  路  3Comments

laurentpierson picture laurentpierson  路  3Comments

drmikecrowe picture drmikecrowe  路  3Comments

3plusalpha picture 3plusalpha  路  3Comments