UE 5.5 Transient Crash in SendAllEndOfFrameUpdates() – Fix from UE 5.6

We’re preparing to ship our game on Unreal Engine 5.5, and while stability has been excellent overall — with multiple 2–4 hour playtests running smoothly — we kept encountering a transient crash during our warm-up phase.

Today I finally caught it in a shipping build with symbols and traced it back to:

cpp

CopyEdit

UWorld::SendAllEndOfFrameUpdates()

Specifically, the issue is caused by ComponentsThatNeedPreEndOfFrameSync being modified while it’s being iterated. This is likely due to render proxy-related updates like cloth simulation triggering changes mid-loop.

Looking into the UE 5.6 branch, it seems Epic already addressed this. However, I doubt this fix will be backported to 5.5 — so here it is for anyone who needs it:


:firecracker: The Original (UE 5.5) – Problematic

cpp

CopyEdit

// Wait for tasks that are generating data for the render proxies, but are not awaited in any TickFunctions 
// E.g., see cloth USkeletalMeshComponent::UpdateClothStateAndSimulate
for (UActorComponent* Component : ComponentsThatNeedPreEndOfFrameSync)
{
	if (Component)
	{
		check(!IsValid(Component) || Component->GetMarkedForPreEndOfFrameSync());
		if (IsValid(Component))
		{
			Component->OnPreEndOfFrameSync();
		}
		FMarkComponentEndOfFrameUpdateState::ClearMarkedForPreEndOfFrameSync(Component);
	}
}
ComponentsThatNeedPreEndOfFrameSync.Reset();

:white_check_mark: Fixed Version (From UE 5.6)

cpp

CopyEdit

// Wait for tasks that are generating data for the render proxies, but are not awaited in any TickFunctions 
// E.g., see cloth USkeletalMeshComponent::UpdateClothStateAndSimulate
static TArray<TObjectPtr<UActorComponent>> LocalComponentsThatNeedPreEndOfFrameSync;
{
	QUICK_SCOPE_CYCLE_COUNTER(STAT_PostTickComponentUpdate_PreEndOfFrameSync_Gather);
	check(IsInGameThread() && !LocalComponentsThatNeedPreEndOfFrameSync.Num());
	LocalComponentsThatNeedPreEndOfFrameSync.Append(ComponentsThatNeedPreEndOfFrameSync.Array());
}

for (UActorComponent* Component : LocalComponentsThatNeedPreEndOfFrameSync)
{
	if (Component)
	{
		check(!IsValid(Component) || Component->GetMarkedForPreEndOfFrameSync());
		if (IsValid(Component))
		{
			Component->OnPreEndOfFrameSync();
		}
		FMarkComponentEndOfFrameUpdateState::ClearMarkedForPreEndOfFrameSync(Component);
	}
}
LocalComponentsThatNeedPreEndOfFrameSync.Reset();
ComponentsThatNeedPreEndOfFrameSync.Reset();

This change avoids modifying the TSet while iterating over it by snapshotting to a local array first — a clean and thread-safe fix.

If you’re shipping on UE 5.5 and running into weird, hard-to-reproduce crashes in SendAllEndOfFrameUpdates(), this patch will likely solve it.

Hope this helps someone avoid a few hours of head-scratching.

– Teella
TeeKru Games | A.L.A.R.M.

2 Likes