Quick answer: Flax Engine runs C# scripts on a .NET runtime over a C++ core, so crashes arrive as either managed exceptions with clean stack traces or native access violations with raw addresses. Hook the script layer for managed exceptions, attach platform and GPU context, and report both kinds to one dashboard so you can tell scripting bugs apart from engine or driver faults.
Flax Engine is a younger entrant that pairs a high-performance C++ core with C# scripting on a .NET runtime, giving indie teams a modern workflow with visual scripting and source access. That dual-layer design shapes how it crashes. A bug in your gameplay C# throws a managed exception with a readable stack trace, while a fault in native code, a plugin, or a GPU driver surfaces as an access violation with little more than an address. Reporting only one layer leaves you blind to the other. This post covers how Flax crashes split across managed and native code and how to capture both with enough context to act.
The managed and native split
Most of your game logic in Flax lives in C# scripts derived from Script, running on the embedded .NET runtime. When that code throws, you get a System.Exception subclass with a managed stack trace naming your methods and line numbers, much like any .NET application. These are the friendly crashes: NullReferenceException on an actor reference cleared by a destroy call, IndexOutOfRangeException in a gameplay array, or an InvalidOperationException from touching the scene during the wrong update phase.
Beneath that sits the C++ engine. Faults there, in rendering, physics, audio, or a native plugin, do not raise managed exceptions. They appear as access violations, segmentation faults, or assertion failures with raw memory addresses and an engine-level callstack rather than your script names. The two categories need different capture strategies, and conflating them is why some Flax crashes feel impossible to diagnose: you are reading a managed-only log for a native fault, or the reverse.
Capturing managed C# exceptions
For the managed layer, .NET gives you the tools you already know. AppDomain.CurrentDomain.UnhandledException catches exceptions that escape your scripts, and you can subscribe to TaskScheduler.UnobservedTaskException for failures inside async work that Flax does not surface otherwise. Inside your script base classes, wrapping update entry points in try and catch lets you record the active scene, the actor involved, and gameplay state before re-throwing or recovering, which turns a bare NullReferenceException into something you can place in your game.
Capture the full exception including InnerException, because Flax and .NET often wrap the original fault. Record the script type, the actor or prefab name, and the current level so a stack trace gains meaning. Managed exceptions are the ones most likely to be your own logic, so prioritizing clean capture here pays off quickly: these are reproducible once you know which actor and scene produced them, and the managed trace usually points straight at the offending line.
Dealing with native faults
Native crashes are harder because the .NET handlers never see them. An access violation in the C++ core or a native plugin terminates the process, and on Windows you may get a minidump if you have configured one. The most actionable signal you can attach from the managed side is a breadcrumb trail: log the last few significant actions, the active scene, and the most recent script calls into native systems, so that when the process dies you at least know what it was doing.
GPU driver faults deserve special attention. Flax targets Vulkan and Direct3D, and a shader compile failure, a device removal, or a driver bug can present as a native crash that clusters on particular hardware. Recording the renderer backend, GPU model, and driver version with every session means a wave of native faults can be traced to one driver family. You cannot always get a clean callstack from native code, but context and breadcrumbs make even an address-only crash narrowable to a subsystem.
Context that makes Flax crashes tractable
Regardless of layer, the same context fields turn noise into signal. Capture the Flax Engine version and your build version, the platform and OS, the GPU and driver, available memory, and the active scene or level. Because Flax exposes source, your engine version matters more than in a black-box engine: a crash on one Flax build may be fixed in the next, and knowing the exact version stops you chasing a bug the engine team already closed.
Add gameplay-specific custom fields that match how your game is structured: the current objective, save slot, or game mode. For a physics-heavy title, the number of active rigid bodies at crash time can explain a native physics fault. The goal is that every report, managed or native, carries enough about the world state that you can reconstruct the scenario without a video. That context is what separates a fixable Flax crash from a one-line mystery you file under cannot reproduce.
Setting it up with Bugnet
Bugnet handles the Flax split naturally because you feed it from both layers. From your managed handlers you send the C# exception, inner exceptions, and stack trace along with scene and actor context, so it arrives as a readable crash. For native faults, you flush the breadcrumb trail and platform context you have been accumulating, so even an address-only access violation lands with the subsystem, GPU, and last actions attached. An in-game report button lets players flag non-fatal glitches while Bugnet captures the current scene and state automatically.
On the dashboard, Bugnet groups duplicate reports into single issues with occurrence counts, so a NullReferenceException hitting many players is one ranked item, and a native fault clustering on a Vulkan driver is another. Custom fields for engine version, GPU, and game mode let you filter to see whether a crash is managed logic you own or a native issue tied to hardware or a Flax version. That separation is exactly what the dual-layer engine demands, and it keeps you fixing the crashes you can actually reach first.
A workflow for a source-available engine
Because Flax ships source, your crash workflow should include version discipline. Pin the engine version per release, note it in every report, and when you update Flax, watch whether known crash signatures disappear. That feedback loop tells you which native faults were engine bugs versus your own, and it keeps you from rewriting working code to dodge a problem an engine update already solved. Treat the managed crashes as your responsibility and the native ones as a triage decision between your plugin code, the engine, and drivers.
Before shipping, deliberately trigger one managed exception and one native fault on each platform you target and confirm both reach your dashboard with context intact. Test GPU paths on more than one vendor, since native rendering crashes love to hide on a single driver. With both layers reporting, grouped and tagged by version and hardware, a young engine like Flax becomes far less risky to ship on, because your post-launch picture is a prioritized list instead of a guess.
Flax crashes live in two worlds: managed C# and native C++. Capture each on its own terms, tag the engine version, and grouping will tell you which world to fix.