Package
Sentry
.NET Flavor
IL2CPP
.NET Version
2.1 (Unity 2021.3.x)
OS
Browser
OS Version
Chrome 148.0.7778.97 (Official Build) (arm64) MacOS 26.4.1
Development Environment
Rider 2025.x (macOS)
Other Error Monitoring Solution
No
Other Error Monitoring Solution Name
No response
SDK Version
4.3.1
Self-Hosted Sentry Version
No response
Workload Versions
Unity 2021.3.24f1
emscripten (whatever ships with Unity 2021.3.x WebGL — 2.0.x range)
Build settings: IL2CPP, WebGL exception support ExplicitlyThrownExceptionsOnly, Brotli compression
react-unity-webgl v10 on the JS side (drives unityInstance.Quit())
UseSentry or SentrySdk.Init call
Auto-initialized via Sentry Unity's SentryOptions.asset ScriptableObject (the default Sentry Unity SDK init path — no manual SentrySdk.Init() call in user code). Options Configuration is wired to a SentryOptionsConfiguration : Sentry.Unity.SentryOptionsConfiguration subclass that sets options.EnableBackpressureHandling = false; inside #if UNITY_WEBGL && !UNITY_EDITOR — that one line is the workaround.
Steps to Reproduce
- Create a Unity 2021.3.x project with Sentry Unity 4.x installed (default settings — EnableBackpressureHandling = true is the .NET 6.0 default).
- Build for WebGL target.
- Embed the build in a web page via react-unity-webgl (or any wrapper that calls unityInstance.Quit() on page navigation).
- Load the page; let Unity fully initialize.
- Trigger unityInstance.Quit() — e.g. press the browser Back button on a page that has a popstate listener wired to call Quit(), or close the tab, or navigate away programmatically.
- Observe: the returned Quit() Promise never resolves; the Unity main loop never returns.
Expected Result
unityInstance.Quit() resolves; the page navigates cleanly.
Actual Result
The page wedges indefinitely. The tab appears frozen (no UI updates, the URL may have already updated in the address bar). The user must force-reload to recover. No exception is thrown, no Sentry event is emitted, no console error appears.
Root cause (analysis included for triage convenience, AI generated):
BackpressureMonitor.Dispose() in Internal/BackpressureMonitor.cs deadlocks on single-threaded platforms:
public void Dispose()
{
try
{
_cts.Cancel();
_workerTask.Wait(); // ← blocks main thread on WebGL
}
...
}
The monitor's worker is started via Task.Run(() => DoWorkAsync(_cts.Token)) and loops on await Task.Delay(10s, ct).
On iOS/Android/Windows this works because Task.Run schedules on a real thread-pool worker. The main thread calls _cts.Cancel(), the worker thread observes cancellation from inside Task.Delay, throws OperationCanceledException, and the task completes — Wait() returns.
On Unity WebGL (single-threaded emscripten) Task.Run schedules on the same main loop as Unity. Task.Delay's continuation is also scheduled on that main loop. When Quit() triggers Dispose(), _workerTask.Wait() synchronously blocks the main thread waiting for a continuation that can only run on the main thread. Classic deadlock.
Confirmed by single-line ablation: setting options.EnableBackpressureHandling = false on WebGL avoids constructing the monitor at all and eliminates the wedge. Bisected version-wise: 3.2.4 (pre-BackpressureMonitor) ✅, 4.0.0 ❌, 4.3.1 ❌, 4.3.1 + workaround ✅.
Package
Sentry
.NET Flavor
IL2CPP
.NET Version
2.1 (Unity 2021.3.x)
OS
Browser
OS Version
Chrome 148.0.7778.97 (Official Build) (arm64) MacOS 26.4.1
Development Environment
Rider 2025.x (macOS)
Other Error Monitoring Solution
No
Other Error Monitoring Solution Name
No response
SDK Version
4.3.1
Self-Hosted Sentry Version
No response
Workload Versions
Unity 2021.3.24f1
emscripten (whatever ships with Unity 2021.3.x WebGL — 2.0.x range)
Build settings: IL2CPP, WebGL exception support ExplicitlyThrownExceptionsOnly, Brotli compression
react-unity-webgl v10 on the JS side (drives unityInstance.Quit())
UseSentry or SentrySdk.Init call
Auto-initialized via Sentry Unity's SentryOptions.asset ScriptableObject (the default Sentry Unity SDK init path — no manual SentrySdk.Init() call in user code). Options Configuration is wired to a SentryOptionsConfiguration : Sentry.Unity.SentryOptionsConfiguration subclass that sets options.EnableBackpressureHandling = false; inside #if UNITY_WEBGL && !UNITY_EDITOR — that one line is the workaround.
Steps to Reproduce
Expected Result
unityInstance.Quit() resolves; the page navigates cleanly.
Actual Result
The page wedges indefinitely. The tab appears frozen (no UI updates, the URL may have already updated in the address bar). The user must force-reload to recover. No exception is thrown, no Sentry event is emitted, no console error appears.
Root cause (analysis included for triage convenience, AI generated):
BackpressureMonitor.Dispose() in Internal/BackpressureMonitor.cs deadlocks on single-threaded platforms:
The monitor's worker is started via Task.Run(() => DoWorkAsync(_cts.Token)) and loops on await Task.Delay(10s, ct).
On iOS/Android/Windows this works because Task.Run schedules on a real thread-pool worker. The main thread calls _cts.Cancel(), the worker thread observes cancellation from inside Task.Delay, throws OperationCanceledException, and the task completes — Wait() returns.
On Unity WebGL (single-threaded emscripten) Task.Run schedules on the same main loop as Unity. Task.Delay's continuation is also scheduled on that main loop. When Quit() triggers Dispose(), _workerTask.Wait() synchronously blocks the main thread waiting for a continuation that can only run on the main thread. Classic deadlock.
Confirmed by single-line ablation: setting options.EnableBackpressureHandling = false on WebGL avoids constructing the monitor at all and eliminates the wedge. Bisected version-wise: 3.2.4 (pre-BackpressureMonitor) ✅, 4.0.0 ❌, 4.3.1 ❌, 4.3.1 + workaround ✅.