Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix(core): enter parallel default configuration when targeting an unvisited history child#5589

Merged
davidkpiano merged 1 commit into
statelyai:mainfrom
spokodev:fix/history-parallel-empty-default
Jul 2, 2026
Merged

fix(core): enter parallel default configuration when targeting an unvisited history child#5589
davidkpiano merged 1 commit into
statelyai:mainfrom
spokodev:fix/history-parallel-empty-default

Conversation

@spokodev

@spokodev spokodev commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Targeting a history state that is a direct child of a parallel state silently does nothing when that parallel state has not been visited yet and the history state has no default target. The machine stays in the source state instead of entering the parallel state's initial configuration, and no error is thrown.

const machine = createMachine({
  initial: 'off',
  states: {
    off: { on: { GO: 'on.hist' } },
    on: {
      type: 'parallel',
      states: {
        regA: { initial: 'a1', states: { a1: {}, a2: {} } },
        regB: { initial: 'b1', states: { b1: {}, b2: {} } },
        hist: { type: 'history', history: 'deep' }
      }
    }
  }
});

const actor = createActor(machine).start();
actor.send({ type: 'GO' });
actor.getSnapshot().value; // stays 'off'; expected { on: { regA: 'a1', regB: 'b1' } }

Cause

In resolveHistoryDefaultTransition (packages/core/src/stateUtils.ts), a history node with no configured target returns parent.initial. For a compound parent that is the single initial child, so entry works. For a parallel parent there is no single initial child, so parent.initial produces a transition with target: []; the empty target set flows through the entry machinery and the transition no-ops. The compound case is already documented and tested ("should execute actions of the initial transition when a history state without a default target is targeted and its parent state was never visited yet"); the parallel case was missed.

Fix

When the history node has no default target and its parent is a parallel state, return the parallel state itself as the target. The existing entry logic then enters every region at its initial state, which is the parallel state's initial configuration. Compound parents are unchanged.

Testing

Added deep and shallow history variants to packages/core/test/history.test.ts. They fail on the current code (value stays off) and pass with the fix. pnpm test:core stays green (1746 passed) and packages/core typechecks clean. Changeset included.

… child

Targeting a history state that is a direct child of a parallel state,
when the parallel state had not been visited yet and the history state
had no default target, resolved to an empty target set and silently
dropped the transition. Fall back to the parallel state itself so its
initial configuration is entered, matching the compound-state case.
@changeset-bot

changeset-bot Bot commented Jul 2, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 9d60a51

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
xstate Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@davidkpiano davidkpiano merged commit e913eeb into statelyai:main Jul 2, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants