UE5 Stall Detector | False GUI Stalls | Exception Thrown ntdll.dll

Table of Contents

Unreal Engine’s Stall Detector As The Core Issue

When integrating external GUI systems (such as VST plugin editors) into Unreal Engine, you may observe:

  • Visual Studio breaking with 0xC0000005 Access Violation

  • Call stacks containing “StackWalk"
  • Threads containing StallDetectorThread

  • Stack walking via RtlVirtualUnwind

  • Breaks only while debugging

  • No crash when debugger is detached

This is not a real crash.

This is Unreal Engine’s Stall Detector misclassifying valid external modal/message-loop activity as a thread stall, followed by the debugger interrupting during diagnostic stack capture.

What the Stall Detector Actually Does

The Stall Detector is an instrumentation system used by Unreal to identify threads that appear unresponsive.

It works by wrapping critical engine scopes with timers. If execution exceeds a configured budget (often ~2 seconds), Unreal assumes the thread may be deadlocked and attempts to gather diagnostic information.

Importantly:

A stall is not automatically a bug.
It is a heuristic.

The engine cannot distinguish between:

  • a thread that is truly frozen

  • a thread that is busy inside an external modal loop

So it errs on the side of reporting.

				
					SCOPE_STALL_COUNTER(SomeScope, TimeBudgetSeconds)


				
			
				
					
//eg main Engine loop, gives your process 2 seconds to respond
void FEngineLoop::Tick()
{
	SCOPED_NAMED_EVENT_TEXT("FEngineLoop::Tick", FrameColor);
	SCOPE_STALL_COUNTER(FEngineLoop::Tick, 2.0);
}
				
			

If a scope exceeds its time budget (often ~2 seconds), the engine:

  1. Assumes the thread may be stuck.

  2. Attempts to capture that thread’s call stack.

  3. Logs diagnostic information.

This is meant to catch deadlocks and infinite loops.

Why External UI Systems Trigger False Stalls

Many UI frameworks — including plugin GUIs — do not follow Unreal’s frame-driven execution model.

Instead, they often:

  • run modal loops

  • pump their own Windows messages

  • block until a popup closes

  • perform synchronous UI work

Examples include:

  • dropdown menus

  • preset browsers

  • file dialogs

  • tooltips

  • blocking progress windows

From Unreal’s perspective, the monitored thread stops ticking engine code.

So the detector concludes:

“Thread appears stalled.”

But the thread is actually busy — just not inside Unreal.

This is a classic false positive.

Where the Access Violation Comes From

After detecting a stall, Unreal attempts to capture a stack trace for diagnostics.

Stack walking through native binaries — especially optimized or protected ones — is not always clean. During this process Windows may raise a structured exception such as:

				
					0xC0000005 Access Violation

				
			

These exceptions are frequently handled internally.

However, when Visual Studio is configured to break on thrown Win32 exceptions, it interrupts execution before the handler runs.

This makes the situation look like a crash when it is not.

Detach the debugger, and everything continues normally.

Important: This Is Not Specific to VST Plugins

VST GUIs are simply a very visible example because they ship with full custom UI frameworks.

But the same behavior can occur with:

  • native Win32 dialogs

  • external editors

  • proprietary UI toolkits

  • embedded browser views

  • platform file pickers

The root cause is the mismatch between:

Unreal’s tick-based thread expectations
and
externally-driven modal/message loops

Confirming the Issue

A quick sanity check:

If the application runs correctly when the debugger is detached, you are not dealing with a fatal crash.

You are observing debugger behavior during a handled exception.

Solution Options

There are three practical solutions, plus one advanced option.

Solution 1 — Pause the Stall Detector (When You Control the Blocking Scope)

Unreal provides an explicit “ignore stall detector” macro.

				
					SCOPE_STALL_DETECTOR_PAUSE();

				
			

This tells the engine:

“We are about to perform a possibly long blocking operation in this scope {}. Do not treat this time as a stall.”

Epic uses this internally when launching Slate modal loops/windows.

However — and this is critical —

It only works if you control the scope that blocks.

Examples where it works well:

  • launching your own modal window

  • blocking editor tools

  • synchronous operations you initiate

  • custom dialogs

Examples where it does NOT help:

  • plugin-created dropdowns

  • internal UI modal loops

  • third-party toolkits

  • code paths you do not own or have control over

In those cases, you cannot wrap the blocking period.

Solution 2 — Disable Breaking on Thrown Access Violations (Recommended)

In Visual Studio Open:

				
					Debug → Windows → Exception Settings

				
			

Locate:

				
					Win32 Exceptions → 0xC0000005 Access violation

				
			

You want to untick this option. This will avoid the debugger getting triggered when modal windows launch.

Why this is safe

This does NOT hide real crashes.

Visual Studio will still break when an access violation is unhandled and would terminate the process.

You can verify this easily:
With the option unticked try to do something illegal like this Access Violation:

				
					UObject* Obj = nullptr;
Obj->GetName();

				
			

The debugger still stops immediately.

What you are disabling is early interruption on exceptions that are later handled internally.

Many large native systems intentionally raise handled exceptions.

Breaking on all of them makes debugging nearly impossible.

Here’s how to confirm:
Enable ALL win32 Exceptions. Then try to launch the UE Editor, it will keep triggering a lot of exceptions itself. You can right click “Win32 Exceptions” and Reset To Default After.

Solution 3 — Detach the Debugger During UI Interaction

A simple workflow improvement:

  • Attach debugger for code work

  • Detach when stress-testing external GUI interaction

This avoids debugger interruption while preserving normal runtime behavior.

4 — Hook Native Window Events

It is technically possible to monitor or intercept Win32 window events (HWND message hooks) to better coordinate with Unreal’s expectations.

For example, detecting when a popup enters a modal loop.

However:

  • This is complex

  • Platform-specific

  • brittle across frameworks

  • rarely worth the maintenance cost

It is generally not recommended unless you are building a very low-level host.

Is This an Editor Problem Only?

In most standard configurations — yes.

The Stall Detector is compiled only when conditions like the following are true:

  • WITH_EDITOR

  • not UE_BUILD_SHIPPING

  • counters tracing enabled

In shipping builds, the detector is typically not active.

So this behavior primarily affects development and debugging, not end users.

Final Perspective

Embedding external native UI into a large engine inevitably creates moments where the engine’s assumptions about thread behavior do not align with reality.

The Stall Detector is doing its job — detecting something unusual.

It simply cannot know that the thread is busy inside another framework.

Understanding this distinction prevents countless hours of chasing non-bugs.

Once properly configured, these interruptions disappear, and your integration can be evaluated based on real stability rather than debugger artifacts.

Rated 5 out of 5

Gamechanger plugin for notehighway rhythm games and music visualizers

Rated 5 out of 5

Add Text and 2D UMG widgets to Level Sequences