Description

Symptom

An intermittent ensureMsgf fires in the editor when a level contains a UGeometryCacheComponent: Array has changed during ranged-for iteration! (TArray ranged-for iterator check, Array.h). The callstack is on the game thread, inside FGeometryCacheSceneProxy::CreateHitProxies, iterating Track->MeshData->BatchesInfo.

Root cause

  1. The FGeometryCacheSceneProxy constructor (GeometryCacheSceneProxy.cpp ~L160-180) decodes the current frame into NewSection->MeshData on the creating thread. It then enqueues {{ENQUEUE_RENDER_COMMAND(FGeometryCacheUpdateAnimation) { SceneProxy->FrameUpdate(RHICmdList); }

    }}.

  2. After CreateSceneProxy() returns, the scene constructs FPrimitiveSceneInfo. When GIsEditor is true (editor and PIE), this calls Proxy->CreateHitProxies (PrimitiveSceneInfo.cpp L123/193). That function ranged-fors over Track->MeshData->BatchesInfo (L345).
  3. The render thread may already be running the FrameUpdate from step 1. For a new track, FrameIndex == NextFrameIndex == 1, so FrameUpdate takes the "seek" branch and calls GetMeshData(FrameIndex, *TrackProxy>MeshData). That ends in FCodecV1Decoder::DecodeFrameData (CodecV1.cpp ~L1110): Reader << OutMeshData.BatchesInfo;.
  4. FGeometryCacheMeshBatchInfo has a custom operator<<, so TArray loading takes the per-element path: Empty(SerializeNum) followed by AddDefaulted_GetRef per element. Num transiently drops to 0 and climbs back up while the game thread iterates the same array, and the ranged-for check fires. The final count is usually identical, which is why it is intermittent.

Proposed fix

Don't hand the proxy to the render thread from the constructor. Move the initial FrameUpdate enqueue into an override of FPrimitiveSceneProxy::CreateRenderThreadResources. That runs inside the scene's add-primitive command, after FPrimitiveSceneInfo and its hit proxies exist.

Steps to Reproduce
  1. Launch the editor with a fresh session (the ensure only reports once).
  2. Open any level with a GeometryCache asset with at least 2 batches/materials is loaded or drag a GeometryCache asset from the Content Browser into the level viewport.
  3. Observe the ensure Array has changed during ranged-for iteration! with a game-thread stack through FGeometryCacheSceneProxy::CreateHitProxies.
  4. If the ensure doesn’t occur, try adding a FPlatformProcess::Sleep(0.5f); in FGeometryCacheMeshBatchInfo operator<< and FGeometryCacheSceneProxy::CreateHitProxies when iterating over the Track->MeshData->BatchesInfo

Expected
No Ensure fires

Actual
An Ensure sometimes fires due to a data race

Have Comments or More Details?

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

0
Login to Vote

Duplicate
ComponentUE - Rendering - Architecture
Affects Versions5.6, 5.7, 5.8
CreatedOct 13, 2026
ResolvedOct 16, 2026
UpdatedOct 19, 2026
View Jira Issue