Description

FRegisterComponentContext::Process() in ActorComponent.cpp iterates over SendRenderDynamicDataPrimitives and calls SendRenderDynamicData_Concurrent() on each. The existing guard checks ::IsValid(Primitive), which catches destroyed/pending-kill objects but not the case where the render state has been torn down while the object is still alive. SendRenderDynamicData_Concurrent() has check(bRenderStateCreated) at the top, which fires intermittently when a component's render state is destroyed between being queued and processed.

The proposed fix is to add Primitive->IsRenderStateCreated() to the existing validity check (Engine/Source/Runtime/Engine/Private/Components/ActorComponent.cpp, ~line 256):

if (::IsValid(Primitive) && Primitive->IsRenderStateCreated())
{
    Primitive->SendRenderDynamicData_Concurrent();
}

This is consistent with the pattern already used in the AddPrimitiveBatches loop ~20 lines above in the same function, which checks Component->IsRenderStateCreated() before operating on the component. A similar pattern existed in the now-deleted AsyncRegisterLevelContext.cpp. That file has been fully removed (deprecated in 5.8).

Steps to Reproduce

No consistent repro. The check fires occasionally while playing the game.

Potential scenarios:

Component Unregistration During Incremental Registration

  1. Actor A's component registersExecuteRegisterEvents()CreateRenderState_Concurrent(Context)Context->AddPrimitive(this) (batched into AddPrimitiveBatches)
  2. Process() runs (either from batch size threshold or OnIncrementalRegisterComponentsDone) → processes AddPrimitiveBatchesScene->AddPrimitive() which can trigger MarkRenderDynamicDataDirty() → component gets queued into SendRenderDynamicDataPrimitives for a future Process() call
  3. Between Process() calls, another actor in the same incremental loop triggers PostRegisterAllComponents()NotifyPostRegisterAllActorComponents() → game code reacts and unregisters component A (e.g., swaps a mesh, replaces a component, calls ReregisterComponent())
  4. Unregistration calls ExecuteUnregisterEvents()DestroyRenderState_Concurrent()bRenderStateCreated = false — but the component is still a valid UObject, still in SendRenderDynamicDataPrimitives
  5. Next Process()::IsValid(Primitive) passes (object is alive) → SendRenderDynamicData_Concurrent()check(bRenderStateCreated)

RerunConstructionScripts After Incremental Registration

From Level.cpp:

OnIncrementalRegisterComponentsDone() → Process()  // flushes current batches

After that, IncrementalRunConstructionScripts() runs, which calls Actor->RerunConstructionScripts(). This destroys and recreates components — including their render state. If a component was already in SendRenderDynamicDataPrimitives from a prior Process() but hadn't been consumed yet (e.g., re-queued via the batch-size threshold path), it could have its render state destroyed by construction script re-execution.

RecreateRenderState_Concurrent Called During Registration

RecreateRenderState_Concurrent() calls DestroyRenderState_Concurrent() then CreateRenderState_Concurrent(). If something triggers a render state recreation (e.g., material change, LOD change, mesh swap) on a component that's already queued in SendRenderDynamicDataPrimitives, the destroy half sets bRenderStateCreated = false. Even though it gets recreated immediately after, the queue still holds a stale reference to a moment when it expected the old render state.

This is less likely to trigger the check since CreateRenderState_Concurrent sets it back to true, but there's a window if Process() runs between the destroy and recreate on another thread.

Editor Property Changes (PreEditChange/PostEditChange)

In PreEditChange() and PostEditChange() , if the component is found to be invalid, ExecuteUnregisterEvents() is called directly — tearing down render state without destroying the UObject. The component could still be in the dynamic data queue from a prior registration batch.

Have Comments or More Details?

There's no existing public thread on this issue, so head over to Questions & Answers just mention UE-380279 in the post.

0
Login to Vote

Unresolved
ComponentUE - Rendering - Architecture - RHI
Affects Versions5.65.75.8
CreatedMay 18, 2026
UpdatedMay 20, 2026
View Jira Issue