We are seeing a crash when running in “PIE Standalone” where it seems that NDCs are cleaned up twice. The crash happens when we hit “Escape” or click Stop and try to exit the PIE level. This does not happen when running in “PIE Play as Client”. This is happening on our custom 5.6.1 engine, but we don’t see any evidence to suggest this is from our changes.
I can see a string of CLs in 5.7 that look like they were trying to address this problem, but it doesn’t look like a definitive fix. Lots of comments hinting at a more permanent fix incoming.
Is this a known issue? Have there been any updates or permanent fixes for this? In the meantime, I’ve put in a band-aid to stop the crash, but it doesn’t stop the fact that for some reason NDCs are getting cleaned up twice.
Band-aid: Just check if the Value parameter is valid before calling cleanup
void FNiagaraDataChannelManager::Cleanup()
{
for (auto& ChannelPair : Channels)
{
if (ChannelPair.Value) <-- Added if check to stop crash, but doesn't fix core problem
{
ChannelPair.Value->Cleanup();
}
}
Channels.Empty();
bIsCleanedUp = true;
}
[Attachment Removed]
I uploaded the callstacks when filling out the question form, but I don’t see them now.
Here is the callstack of the first cleanup that happens on level close:
FNiagaraDataChannelManager::Cleanup() NiagaraDataChannelManager.cpp:60
[Inlined] Invoke(void (*const &)(UWorld *), UWorld *&&) Invoke.h:47
[Inlined] UE::Core::Private::Tuple::TTupleBase::ApplyAfter(void (*const &)(UWorld *), UWorld *&&) Tuple.h:320
TBaseStaticDelegateInstance::ExecuteIfSafe(UWorld *) DelegateInstancesImpl.h:799
[Inlined] TMulticastDelegateBase::Broadcast(UWorld *) MulticastDelegateBase.h:258
TMulticastDelegate::Broadcast(UWorld *) DelegateSignatureImpl.inl:1080
UEditorEngine::TeardownPlaySession(FWorldContext &) PlayLevel.cpp:752
UEditorEngine::EndPlayMap() PlayLevel.cpp:370
UEditorEngine::Tick(float, bool) EditorEngine.cpp:2541
UUnrealEdEngine::Tick(float, bool) UnrealEdEngine.cpp:530
FEngineLoop::Tick() LaunchEngineLoop.cpp:5807
[Inlined] EngineTick() Launch.cpp:60
GuardedMain(const wchar_t *) Launch.cpp:200
LaunchWindowsStartup(HINSTANCE__ *, HINSTANCE__ *, char *, int, const wchar_t *) LaunchWindows.cpp:294
WinMain(HINSTANCE__ *, HINSTANCE__ *, char *, int) LaunchWindows.cpp:364
[Inlined] invoke_main() 0x00007ff72dcaacea
__scrt_common_main_seh() 0x00007ff72dcaacc9
<unknown> 0x00007ffc859ae957
<unknown> 0x00007ffc8640427c
Here is the crash callstack on the 2nd attempted cleanup:
FNiagaraDataChannelManager::Cleanup() NiagaraDataChannelManager.cpp:66
FNiagaraWorldManager::OnWorldCleanup(bool, bool) NiagaraWorldManager.cpp:847
FNiagaraWorldManager::~FNiagaraWorldManager() NiagaraWorldManager.cpp:478
FNiagaraWorldManager::`scalar deleting destructor'(unsigned int) 0x00007ffb6c871db4
FNiagaraWorldManager::OnPreWorldFinishDestroy(UWorld *) NiagaraWorldManager.cpp:1068
[Inlined] Invoke(void (*const &)(UWorld *), UWorld *&&) Invoke.h:47
[Inlined] UE::Core::Private::Tuple::TTupleBase::ApplyAfter(void (*const &)(UWorld *), UWorld *&&) Tuple.h:320
TBaseStaticDelegateInstance::ExecuteIfSafe(UWorld *) DelegateInstancesImpl.h:799
[Inlined] TMulticastDelegateBase::Broadcast(UWorld *) MulticastDelegateBase.h:258
TMulticastDelegate::Broadcast(UWorld *) DelegateSignatureImpl.inl:1080
UWorld::FinishDestroy() World.cpp:1496
UObject::ConditionalFinishDestroy() Obj.cpp:1312
IncrementalDestroyGarbage(bool, double) GarbageCollection.cpp:4856
IncrementalPurgeGarbage(bool, double) GarbageCollection.cpp:4736
UE::GC::PostCollectGarbageImpl<…>(EObjectFlags) GarbageCollection.cpp:5788
UE::GC::FReachabilityAnalysisState::PerformReachabilityAnalysisAndConditionallyPurgeGarbage(bool) GarbageCollection.cpp:5984
[Inlined] UE::GC::CollectGarbageInternal(EObjectFlags, bool) GarbageCollection.cpp:5540
CollectGarbage(EObjectFlags, bool) GarbageCollection.cpp:6231
UEditorEngine::EndPlayMap() PlayLevel.cpp:529
UEditorEngine::Tick(float, bool) EditorEngine.cpp:2541
UUnrealEdEngine::Tick(float, bool) UnrealEdEngine.cpp:530
FEngineLoop::Tick() LaunchEngineLoop.cpp:5807
[Inlined] EngineTick() Launch.cpp:60
GuardedMain(const wchar_t *) Launch.cpp:200
LaunchWindowsStartup(HINSTANCE__ *, HINSTANCE__ *, char *, int, const wchar_t *) LaunchWindows.cpp:294
WinMain(HINSTANCE__ *, HINSTANCE__ *, char *, int) LaunchWindows.cpp:364
[Inlined] invoke_main() 0x00007ff72dcaacea
__scrt_common_main_seh() 0x00007ff72dcaacc9
<unknown> 0x00007ffc859ae957
<unknown> 0x00007ffc8640427c
[Attachment Removed]
km.stu
(km.stu)
June 17, 2026, 5:46pm
3
Hey,
That seems reasonable, this code was refactored later and has a similar check now.
for (auto& ChannelPair : Channels)
{
if(UNiagaraDataChannelHandler* ChannelHandler = ChannelPair.Value.Get())
{
ChannelHandler->Cleanup();
}
}
Thanks,
Stu
[Attachment Removed]