Summary
FStreamableManager::StartHandleRequests caches raw FStreamable* pointers in a local array, then iterates them in a second loop whose CheckCompletedRequests calls can complete the whole handle synchronously (when all targets are already in memory) and free FStreamable entries mid-loop. Line 2192 then reads, and line 2194 writes, through a stale pointer. Under the default allocator this silently corrupts the heap and manifests later as seemingly random crashes in unrelated subsystems (Slate paint, AssetRegistry, PropertyEditor, allocator internals) — we chased those for a full day before -stompmalloc pinpointed the real site.
What Type of Bug are you experiencing?
Foundation (C++ Tools, Profiling, & Pipeline)
Steps to Reproduce
- In a C++ project, add a UWorldSubsystem override of OnWorldBeginPlay that batch-preloads assets:
PreloadHandle = UAssetManager::GetStreamableManager().RequestAsyncLoad(Paths, FStreamableDelegate(), FStreamableManager::DefaultAsyncLoadPriority, /*bManageActiveHandle=*/false);
wherePathsis aTArray<FSoftObjectPath>of ~150 content assets (animation montages / data assets), andPreloadHandleis aTSharedPtr<FStreamableHandle>member of the subsystem. - Launch the editor with -stompmalloc (to catch the access violation deterministically at the faulting site rather than as delayed heap corruption).
- Enter PIE once (cold load - all targets not yet in memory). This works.
- Exit PIE, then enter PIE again in the same editor session, so that all requested targets are already resident.
Expected Result
RequestAsyncLoad with already-resident targets should complete the handle immediately (as documented by the in-code comment “this may call the callback right away”) without any invalid memory access. No crash, no heap corruption.
Observed Result
On the second PIE run of the same editor session, the editor crashes with an access violation inside FStreamableManager::StartHandleRequests (StreamableManager.cpp:2192), reading a freed FStreamable. Full callstack in the callstack field below. Without -stompmalloc the same defect does not crash at the call site but silently corrupts the heap, causing delayed crashes in unrelated subsystems.
Affects Versions
5.8
Platform(s)
Windows
For crash reports, include your callstack
Unhandled Exception: EXCEPTION_ACCESS_VIOLATION reading address 0x000002c28b951fd0
UnrealEditor_Engine!FStreamableManager::StartHandleRequests() [D:\build++UE5\Sync\Engine\Source\Runtime\Engine\Private\StreamableManager.cpp:2192]
UnrealEditor_Engine!FStreamableManager::RequestAsyncLoad() [D:\build++UE5\Sync\Engine\Source\Runtime\Engine\Private\StreamableManager.cpp:2116]
UnrealEditor_MyGame!FStreamableManager::RequestAsyncLoad<TArray<FSoftObjectPath,TSizedDefaultAllocator<32> > &,TDelegate<void __cdecl(void),FDefaultDelegateUserPolicy>,void>() [Engine\Source\Runtime\Engine\Classes\Engine\StreamableManager.h:1045]
UnrealEditor_MyGame!UMyPreloadSubsystem::OnWorldBeginPlay() [MyGame\Source\MyGame\MyPreloadSubsystem.cpp:54]
UnrealEditor_Engine!UE::Core::Private::Function::TFunctionRefCaller<UWorld::BeginPlay'::2’::<lambda_1>,void,UWorldSubsystem *>::Call() [D:\build++UE5\Sync\Engine\Source\Runtime\Core\Public\Templates\Function.h:292]
UnrealEditor_Engine!UE::Core::Private::Function::TFunctionRefCaller<FObjectSubsystemCollection<UWorldSubsystem>::ForEachSubsystem'::2’::<lambda_1>,void,USubsystem *>::Call() [D:\build++UE5\Sync\Engine\Source\Runtime\Core\Public\Templates\Function.h:298]
UnrealEditor_Engine!FSubsystemCollectionBase::ForEachSubsystemOfClass() [D:\build++UE5\Sync\Engine\Source\Runtime\Engine\Private\Subsystems\SubsystemCollection.cpp:178]
UnrealEditor_Engine!UWorld::BeginPlay() [D:\build++UE5\Sync\Engine\Source\Runtime\Engine\Private\World.cpp:6171]
UnrealEditor_Engine!UGameInstance::StartPlayInEditorGameInstance() [D:\build++UE5\Sync\Engine\Source\Runtime\Engine\Private\GameInstance.cpp:568]
UnrealEditor_UnrealEd!UEditorEngine::CreateInnerProcessPIEGameInstance() [D:\build++UE5\Sync\Engine\Source\Editor\UnrealEd\Private\PlayLevel.cpp:3200]
UnrealEditor_UnrealEd!UEditorEngine::OnLoginPIEComplete_Deferred() [D:\build++UE5\Sync\Engine\Source\Editor\UnrealEd\Private\PlayLevel.cpp:1623]
UnrealEditor_UnrealEd!UEditorEngine::CreateNewPlayInEditorInstance() [D:\build++UE5\Sync\Engine\Source\Editor\UnrealEd\Private\PlayLevel.cpp:1886]
UnrealEditor_UnrealEd!UEditorEngine::StartPlayInEditorSession() [D:\build++UE5\Sync\Engine\Source\Editor\UnrealEd\Private\PlayLevel.cpp:2928]
UnrealEditor_UnrealEd!UEditorEngine::StartQueuedPlaySessionRequestImpl() [D:\build++UE5\Sync\Engine\Source\Editor\UnrealEd\Private\PlayLevel.cpp:1210]
UnrealEditor_UnrealEd!UEditorEngine::StartQueuedPlaySessionRequest() [D:\build++UE5\Sync\Engine\Source\Editor\UnrealEd\Private\PlayLevel.cpp:1111]
UnrealEditor_UnrealEd!UEditorEngine::Tick() [D:\build++UE5\Sync\Engine\Source\Editor\UnrealEd\Private\EditorEngine.cpp:2065]
UnrealEditor_UnrealEd!UUnrealEdEngine::Tick() [D:\build++UE5\Sync\Engine\Source\Editor\UnrealEd\Private\UnrealEdEngine.cpp:546]
UnrealEditor!FEngineLoop::Tick() [D:\build++UE5\Sync\Engine\Source\Runtime\Launch\Private\LaunchEngineLoop.cpp:5859]
UnrealEditor!GuardedMain() [D:\build++UE5\Sync\Engine\Source\Runtime\Launch\Private\Launch.cpp:190]
UnrealEditor!GuardedMainWrapper() [D:\build++UE5\Sync\Engine\Source\Runtime\Launch\Private\Windows\LaunchWindows.cpp:123]
UnrealEditor!LaunchWindowsStartup() [D:\build++UE5\Sync\Engine\Source\Runtime\Launch\Private\Windows\LaunchWindows.cpp:277]
UnrealEditor!WinMain() [D:\build++UE5\Sync\Engine\Source\Runtime\Launch\Private\Windows\LaunchWindows.cpp:338]
UnrealEditor!__scrt_common_main_seh() [D:\a_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288]
kernel32
ntdll
Additional Notes
Analysis: the first loop in StartHandleRequests (StreamableManager.cpp:2176-2183) collects raw FStreamable* pointers into a local ExistingStreamables array. The second loop (:2188-2198) dereferences them, but its CheckCompletedRequests calls can synchronously complete the whole handle (all targets already resident) and free FStreamable entries belonging to later indices of the same array. :2192 then reads the freed struct and :2194 writes to it.
Impact without -stompmalloc: silent heap corruption. In our project this produced a full day of seemingly unrelated “random” editor crashes (Slate paint, AssetRegistry, PropertyEditor detail panels, mimalloc internals) before the real site was identified with the stomp allocator - so the severity is much higher than the crash frequency at this callstack suggests.
Related: the same code pattern exists in 5.7. The June 2026 ALT-safety changes to this file (e.g. “Remove unsafe access of streamablehandle/streamable on the ALT”) address cross-thread access, not this same-thread completion path.
Workaround: before calling RequestAsyncLoad, filter out targets whose FSoftObjectPath::ResolveObject() is non-null (already resident) and skip redirectors; if the remaining list is empty, skip the call entirely. With this filter the crash is no longer reachable.
Minidump and full editor log available on request.