Quick answer: Component BeginPlay order isn’t guaranteed. Do per-component init in InitializeComponent, and do cross-component wiring in the owning actor’s BeginPlay (which runs after all components’).

A HealthComponent’s BeginPlay reads a StatsComponent that isn’t initialized yet — sometimes. Component BeginPlay order varies.

The Lifecycle Order

  1. All components’ OnRegister / InitializeComponent (if bWantsInitializeComponent).
  2. Each component’s BeginPlay — order between components is not defined.
  3. The owning Actor’s BeginPlay — runs after every component’s BeginPlay.

Per-Component Init

UMyComponent::UMyComponent()
{
    bWantsInitializeComponent = true;
}

void UMyComponent::InitializeComponent()
{
    Super::InitializeComponent();
    // set up THIS component's own state only
}

Cross-Component Wiring in the Actor

void AMyCharacter::BeginPlay()
{
    Super::BeginPlay();
    // every component's BeginPlay has run by now
    HealthComp->BindToStats(StatsComp);
}

The actor’s BeginPlay is the one place where every component is guaranteed initialized. Do the wiring there.

Don't Rely on Add Order

Even “the order I added them in the Blueprint” isn’t a contract. Treat component BeginPlay as parallel-ish; never have one read another’s BeginPlay-set state.

Verifying

Spawn the character many times. HealthComponent always sees an initialized StatsComponent. No intermittent null/zero reads.

“Component BeginPlay order is undefined. Wire components together in the actor’s BeginPlay.”

If component A truly must init before B, consider making B a child of A or merging them — ordering dependencies are a smell.