Unity and Unreal crash reporting with the Sentry SDK.
Game crash reporting sits at the intersection of three different code layers: managed script code that throws typed exceptions, generated or hand-written C++ that produces raw minidumps, and platform-specific crash collection systems that enforce certification requirements on consoles. This guide covers how to wire up sentry-unity and the Sentry Unreal plugin, how to symbolicate IL2CPP native builds and Unreal shipping builds, how to handle console platforms that cannot upload at crash time, and how to point all of it at urgentry by changing only the DSN.
20 seconds. For Unity: install the sentry-unity UPM package, fill in the DSN in the Sentry configuration asset, enable the IL2CPP scripting backend toggle if you ship IL2CPP builds, and upload symbols with sentry-cli debug-files upload --url https://your-urgentry-host. For Unreal: add the Sentry plugin to your project, set the DSN in Project Settings, add "Sentry" to your module's PublicDependencyModuleNames in the Build.cs file, and upload PDB or dSYM files after each shipping build. Swap the DSN to your urgentry DSN and nothing else changes.
60 seconds. Unity managed builds capture C# exceptions automatically. IL2CPP builds add a layer: managed exceptions still work, but native crashes in the generated C++ require the platform's native crash handler plus a symbol upload so urgentry can read the frames. Unreal's CrashReportClient collects .ue4crash files, which bundle a minidump and metadata. Blueprint errors surface as handled exceptions in the Unreal VM and do not need symbol files; C++ crashes do. Console platforms block network egress at crash time under cert rules; the standard pattern stores crash data on the device and uploads it at the next launch.
The SDK tick cost in a 60 Hz game loop is not free. The sentry-unity package routes crash capture through out-of-process handlers, but breadcrumbs added inside Update() accumulate fast and create memory pressure. GPU driver crashes look like process terminations without a stack trace. And device thermals on mobile can kill a process at the OS level without generating a crash report, producing silent session drops. All three patterns are covered below.
Where game crashes come from
Game crash surfaces split differently than server-side crashes, and the failure mode determines what recovery data you get.
Managed C# exceptions in Unity. Unity's Mono and IL2CPP runtimes catch unhandled C# exceptions before they terminate the process. A NullReferenceException thrown inside a MonoBehaviour's Update() method fires Application.logMessageReceived and generates a log entry. The application keeps running. The Sentry SDK hooks into this callback and converts those exceptions into error events. This is the easiest crash surface: no native symbols needed, and the stack trace includes C# method names.
IL2CPP native crashes. When you build Unity with the IL2CPP scripting backend, your C# code compiles to C++, which is then compiled to native machine code by the platform's C++ compiler. A crash in that generated C++ code, such as a null pointer dereference or a bounds overrun in unsafe code, produces a native crash. The native crash handler captures a minidump or platform crash report. The stack frames in the dump reference addresses in the generated C++ binary. Reading those addresses requires the symbol files from the IL2CPP build: the .dSYM bundle on iOS, the .so debug files on Android, or the .pdb on Windows.
Unreal's Blueprint and native split. Unreal projects run two types of code simultaneously. Blueprint graphs execute inside Unreal's Blueprint VM, which interprets compiled bytecode. C++ game modules and engine code compile to native machine code. A crash in Blueprint produces a Blueprint call stack that the VM captures directly, without requiring native symbol files. A crash in C++ produces a minidump with raw addresses. Both paths end up in urgentry, but they need different data to be readable.
GPU driver crashes. A GPU driver fault terminates the process from outside, bypassing every crash handler. The application exits with no crash report, no minidump, and no stack trace. From the error tracking perspective, the session simply ends. The Sentry SDK detects abnormal session termination and generates a session-ended-abnormally event, but without a stack trace it cannot say why. GPU driver crashes appear as a cluster of abnormal session terminations without accompanying crash events, often correlated with a specific device class or driver version.
Console platform minidumps. Nintendo Switch, PlayStation, and Xbox each have their own crash collection formats and certification requirements. All three ultimately produce minidump-equivalent files, but the file format, the upload path, and the timing of upload differ per platform. Console cert programs restrict when a title can make network calls, which affects how and when crash data reaches urgentry.
Unity SDK install
The sentry-unity package installs through Unity's Package Manager. Open the Package Manager window, select Add package by name, and enter:
io.sentry.unity
Alternatively, add it to your Packages/manifest.json directly:
{
"dependencies": {
"io.sentry.unity": "2.5.0"
},
"scopedRegistries": [
{
"name": "Sentry",
"url": "https://npm.sentry.io/sentry-unity/",
"scopes": ["io.sentry"]
}
]
}
After the package installs, create the Sentry configuration asset via Tools > Sentry > Initialize SDK. This places a SentryOptions ScriptableObject at Assets/Resources/Sentry/SentryOptions.asset. Select that asset in the Inspector and fill in your urgentry DSN:
// What the SentryOptions asset controls at runtime:
// Dsn: your urgentry project DSN
// Release: set this to your build version string
// Environment: "production", "staging", etc.
// EnableIL2CppLineNumbers: true for IL2CPP symbol resolution
Under the Options section of the inspector, enable IL2CPP line numbers if your build targets IL2CPP. This setting tells the native crash handler to collect the additional data needed to resolve IL2CPP frames. Leave it off for Mono-only builds; it adds no value there and slightly increases the native library size.
The SDK initializes before any MonoBehaviour's Awake runs. The initialization order is controlled by Unity's RuntimeInitializeOnLoadMethod attribute inside the package. You do not call any init function in your own code; the package handles startup automatically when the DSN is set in the configuration asset.
Capturing managed exceptions in Unity
After initialization, the SDK captures unhandled C# exceptions without any additional code. Unity routes unhandled exceptions through Application.logMessageReceived with a log type of Exception. The SDK subscribes to this callback and converts matching entries into Sentry error events.
For exceptions you catch and handle yourself, call SentrySdk.CaptureException:
void LoadPlayerData(string playerId)
{
try
{
var data = _dataService.Load(playerId);
ApplyPlayerData(data);
}
catch (SaveFileCorruptedException ex)
{
// Capture it before falling back to default data.
SentrySdk.CaptureException(ex);
ApplyDefaultPlayerData();
}
}
Unity's Application.logMessageReceived fires on the main thread for log entries that originate on the main thread. Log entries from worker threads fire on Application.logMessageReceivedThreaded. The SDK subscribes to both callbacks. If you use Debug.LogException from a worker thread and see events missing in urgentry, verify that logMessageReceivedThreaded is wired; older versions of the package only wired the main-thread callback.
ScriptableObjects have a lifecycle gotcha. A ScriptableObject's OnEnable runs before any scene loads, and if a ScriptableObject throws during OnEnable, the Sentry SDK may not have finished initializing yet. The package's initialization order guarantee applies to the scene load lifecycle; it does not cover exceptions thrown before the first scene loads. For ScriptableObjects that run validation logic in OnEnable, wrap that logic in a try/catch and use Debug.LogException as the fallback to ensure the entry at least reaches the Unity log even if the SDK is not ready.
Breadcrumbs in Unity are most useful when they record game state transitions: level loads, scene changes, significant game events, and network request outcomes. Add them with SentrySdk.AddBreadcrumb. Do not add breadcrumbs inside Update() or FixedUpdate() without a frame-rate limiter; at 60 Hz, a single breadcrumb call per frame fills the 100-breadcrumb buffer in under two seconds.
Symbolicating IL2CPP builds
IL2CPP compiles C# to C++, which the platform compiler then compiles to native machine code. The native crash handler captures raw addresses in that native binary. To read those addresses, you need the symbol files produced during the build and the IL2CPP-generated C++ source files that map generated code back to original C# locations.
After a build, collect the symbol files from the build output. For iOS:
# Upload dSYM bundles after an iOS IL2CPP build
sentry-cli debug-files upload \
--url https://errors.example.com \
--auth-token "$SENTRY_AUTH_TOKEN" \
--org your-org-slug \
--project your-project-slug \
/path/to/Builds/iOS/MyGame.dSYM
For Android, the IL2CPP native libraries are compiled to .so files per ABI. The unstripped versions sit in the build output under Builds/Android/Symbols/:
# Upload unstripped .so files for all ABIs
sentry-cli debug-files upload \
--url https://errors.example.com \
--auth-token "$SENTRY_AUTH_TOKEN" \
--org your-org-slug \
--project your-project-slug \
/path/to/Builds/Android/Symbols/
For Windows standalone builds, upload the PDB files from the build output directory alongside the executable:
sentry-cli debug-files upload \
--url https://errors.example.com \
--auth-token "$SENTRY_AUTH_TOKEN" \
--org your-org-slug \
--project your-project-slug \
/path/to/Builds/Windows/MyGame_Data/Plugins/
On iOS, Apple's App Store processing may recompile your binary from bitcode if your Xcode project has bitcode enabled. If that happens, the dSYM UUID in your local build will not match the binary Apple distributes. Xcode 14 deprecated bitcode for iOS; for projects built with Xcode 14 or later, the locally generated dSYM matches the distributed binary. If you maintain a project built with an older toolchain, retrieve the recompiled dSYMs from App Store Connect and re-upload them with sentry-cli.
For Android IL2CPP builds that use ProGuard or R8 on the Java layer, upload the mapping.txt file as well. The Java exception that wraps an IL2CPP native fault will have obfuscated Java frames above the native frames. Both symbol files are needed to read the full stack.
Unreal SDK install
Download the Sentry Unreal plugin from the Unreal Marketplace or the Sentry GitHub releases page. Place the plugin folder at Plugins/Sentry/ in your project root. The plugin directory structure should look like:
Plugins/
Sentry/
Sentry.uplugin
Source/
Sentry/
Config/
Enable the plugin in your .uproject file:
{
"Plugins": [
{
"Name": "Sentry",
"Enabled": true
}
]
}
In your game module's Build.cs file, add "Sentry" to the public or private dependency list:
public class MyGame : ModuleRules
{
public MyGame(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[]
{
"Core",
"CoreUObject",
"Engine",
"Sentry"
});
}
}
Set the DSN in Project Settings > Plugins > Sentry. Set the DSN field to your urgentry project DSN. Set the Release field to your build version string. The environment field defaults to the Unreal build configuration name (Development, Shipping). Leave it at the default or set it explicitly:
// Programmatic initialization if you prefer code over Project Settings:
#include "SentrySubsystem.h"
void UMyGameInstance::Init()
{
Super::Init();
USentrySubsystem* SentrySubsystem = GetSubsystem<USentrySubsystem>();
if (SentrySubsystem)
{
USentrySettings* Settings = NewObject<USentrySettings>();
Settings->DsnUrl = TEXT("https://<key>@errors.example.com/<project_id>");
Settings->Release = TEXT("mygame@1.4.0");
Settings->Environment = TEXT("production");
SentrySubsystem->InitializeWithSettings(Settings);
}
}
Capturing Unreal crashes
Unreal Engine ships a separate process called CrashReportClient that handles crash data collection and upload. When the Unreal process crashes, the engine's crash handler forks or launches CrashReportClient, which collects a .ue4crash file, a minidump, log excerpts, and metadata, and posts them to the configured crash report endpoint.
The Sentry Unreal plugin configures CrashReportClient to post to Sentry's minidump endpoint. With urgentry, the endpoint is identical: the only value that changes is the upload URL, which the plugin derives from the DSN. You do not manually configure CrashReportClient; the plugin handles the configuration.
The .ue4crash format bundles a Breakpad-format minidump with Unreal-specific metadata. The minidump portion contains the raw stack addresses. To read those addresses for C++ crashes, upload PDB files on Windows or dSYM bundles on macOS/iOS after each shipping build:
# Windows shipping build: upload PDB files
sentry-cli debug-files upload \
--url https://errors.example.com \
--auth-token "$SENTRY_AUTH_TOKEN" \
--org your-org-slug \
--project your-project-slug \
/path/to/Build/Windows/MyGame/Binaries/Win64/
# macOS shipping build: upload dSYM bundles
sentry-cli debug-files upload \
--url https://errors.example.com \
--auth-token "$SENTRY_AUTH_TOKEN" \
--org your-org-slug \
--project your-project-slug \
/path/to/Build/Mac/MyGame.app.dSYM
Blueprint crashes do not require native symbol files. When a Blueprint node throws an error, the Unreal Blueprint VM captures the Blueprint call stack as a sequence of graph names and node references. This information arrives in urgentry as a structured stack trace with human-readable graph and node names. You can read Blueprint crash stacks without any symbol upload step.
For C++ crashes in development builds, Unreal includes symbol information in the binary by default. Shipping builds strip symbols and require the separate PDB or dSYM upload. Verify that your shipping build pipeline includes the symbol upload step before distributing to players; crashes from the first hours of a release will be unsymbolicated if the upload step is missing.
Console platforms
Nintendo Switch, PlayStation 4/5, and Xbox One/Series all use minidump-equivalent crash formats under their platform SDKs. Each platform's crash collection system has differences that affect how you integrate and when data reaches urgentry.
Nintendo Switch. The Switch platform SDK provides a crash dump API that produces an NX-specific crash format. The Sentry SDK for Switch (distributed under NDA alongside Nintendo's SDK) converts this format to the Sentry envelope protocol. Switch cert requirements restrict network egress during crash handling. The standard pattern writes the crash report to the game's save data storage at crash time, then reads and uploads it on the next application launch when the title is in a state that permits network access.
PlayStation 4 and 5. PlayStation uses a minidump format compatible with the Breakpad specification. The platform crash handler writes the dump to the title's writable storage. The Sentry PS4/PS5 SDK reads crash files at launch, formats them as Sentry envelopes, and uploads them to urgentry before the main game loop starts. PlayStation cert rules require that the title not make unapproved network calls in the crash handler itself; the deferred-upload pattern satisfies this requirement.
Xbox One and Xbox Series. Xbox uses Windows-compatible PDB symbol files and produces a Windows MiniDump-format crash file. The Xbox crash handler can upload immediately in some configurations, but cert compliance often requires the same deferred pattern as Switch and PlayStation: write to storage at crash time, upload at the next launch. The Xbox port of the Sentry SDK handles this through the CrashPadHandler or a platform-specific handler depending on the SDK version.
The cert-required distinction matters for symbol uploads as well. All three console platforms require that debug symbols (PDB files, dSYM equivalents) never ship in the distribution build. The symbols stay on the developer's servers and are referenced only at crash analysis time. Upload console platform symbols to urgentry the same way you upload PC or mobile symbols: sentry-cli debug-files upload with the platform's symbol file format.
For all three consoles, the deferred-upload pattern means that urgentry receives the crash event on the player's next session, not at the moment of the crash. Events arrive batched and delayed. urgentry stores them with the original crash timestamp from the device, so the event timeline in urgentry reflects when the crash happened, not when the data arrived.
Pointing all of this at urgentry
The DSN is the only runtime configuration change for both Unity and Unreal. Create a project in urgentry, copy the DSN, and paste it into the Sentry configuration asset (Unity) or Project Settings (Unreal). The DSN format is identical to a Sentry DSN:
https://<public_key>@errors.example.com/<project_id>
For sentry-cli symbol uploads, add the --url flag or set the SENTRY_URL environment variable:
export SENTRY_URL=https://errors.example.com
export SENTRY_AUTH_TOKEN=your_urgentry_auth_token
export SENTRY_ORG=your-org-slug
export SENTRY_PROJECT=your-project-slug
# Now every sentry-cli debug-files upload call targets urgentry
sentry-cli debug-files upload /path/to/symbols/
urgentry implements all 218 Sentry REST API operations, including the full debug-files endpoint suite. Every sentry-cli debug-files upload command that works with Sentry works with urgentry. The minidump endpoint (/api/PROJECT_ID/minidump/) is also covered, which is what the Unreal CrashReportClient uses for its uploads.
For console platforms, the console SDK's DSN or upload URL field is the only change. The SDK itself remains the same. The deferred-upload path reads the DSN from the same configuration location and posts to urgentry's event ingest endpoint on the next launch.
Check the urgentry compatibility matrix for the full list of covered operations if you use less common features such as session replay, user feedback, or profiling.
Performance and battery on mobile and console
The sentry-unity package routes native crash capture through an out-of-process handler, so a crash in the game process does not rely on the same process's memory being intact. The managed exception hook runs in-process, but the cost of the hook call itself is negligible; it fires only when an exception reaches the log callback, which is a rare event in a well-functioning game.
The performance concern is breadcrumbs. The SDK's breadcrumb buffer sits in memory and grows with each call to SentrySdk.AddBreadcrumb. In a 60 Hz game loop, calling AddBreadcrumb once per frame produces 3,600 breadcrumb calls per minute. The buffer holds 100 breadcrumbs by default, so the buffer fills and wraps in under two seconds. The allocation pressure from discarded breadcrumbs, combined with the string allocations for the breadcrumb message, adds measurable GC pressure in C# even though most breadcrumbs are immediately discarded.
The correct pattern is to add breadcrumbs at event boundaries, not at frame boundaries:
// Good: breadcrumb on a state transition
void OnLevelLoaded(string levelName)
{
SentrySdk.AddBreadcrumb(
message: $"Loaded level {levelName}",
category: "game.level",
type: "navigation"
);
}
// Bad: breadcrumb on every physics tick
void FixedUpdate()
{
// Do not do this. 50 calls/second, all discarded.
SentrySdk.AddBreadcrumb($"Player pos: {transform.position}");
}
On mobile, the battery concern is network activity. The SDK batches events and flushes the queue based on event count and time interval. In a game that generates many warning-level log entries, the flush interval fires frequently and the SDK makes network calls in the background. Set the SDK's sampling rate to limit the volume of events sent, and disable automatic breadcrumb capture for HTTP requests if your game makes frequent API calls that add noise without diagnostic value.
On console, the cost model shifts: network calls at runtime are gated by cert requirements, so the SDK writes to storage rather than to the network at crash time. The storage write is the cost to manage. Keep crash payloads small by limiting the breadcrumb count (MaxBreadcrumbs) and attachment size.
Three game-platform gotchas
1. Unity Editor vs build behavior split
The sentry-unity SDK behaves differently in the Unity Editor than in a build. In the Editor, the SDK runs in a reduced mode: some native crash hooks are not installed because the Editor itself is the host process, and crashing the Editor would destroy the development environment. The crash handler for IL2CPP native code is not active in the Editor because Editor builds use Mono, not IL2CPP.
This means you cannot test IL2CPP crash reporting by running in the Editor. Build a Development build for the target platform and test crash capture there. A common mistake is to verify that managed exceptions reach urgentry via the Editor, then ship an IL2CPP build and discover that native crashes do not arrive because the symbol upload step was never set up. Set up the symbol upload in your build pipeline and verify it with a deliberate native crash in a Development build before moving to Shipping.
2. Unreal shipping-vs-development symbol stripping
Unreal's Development configuration builds with debug symbols embedded or alongside the binary, which makes native crash stacks readable without a separate symbol upload. Shipping configuration strips symbols by default, reducing binary size significantly. If your crash reporting pipeline was set up and tested against Development builds, it will appear to work fine. Shipping crashes will arrive in urgentry with raw addresses because the PDB or dSYM files were never uploaded.
Verify that your CI pipeline runs the symbol upload step against Shipping builds, not Development builds. The Shipping PDB or dSYM is a different file with a different build ID than the Development equivalent; uploading the Development symbol file does not help with Shipping crashes.
3. Mobile device thermals causing apparent crashes
On iOS and Android, the OS thermal manager kills processes when the device reaches a thermal threshold. This kill is a SIGKILL from outside the process, with no crash report written, no stack trace, and no minidump. From the Sentry SDK's perspective, the session ends abnormally. The event in urgentry shows as a session drop or an abnormal termination, but with no exception and no stack.
A cluster of abnormal session terminations without accompanying crash events in a specific device class or after a specific playtime suggests thermal kills rather than a code defect. The Sentry iOS SDK's watchdog termination detection fires for these events and labels them as potential watchdog or thermal kills. On Android, the Sentry Android SDK tracks abnormal session ends similarly. Use these signals to identify device classes or game scenes that produce sustained high CPU load, then profile in that context to find the heat source.
Do not add crash detection instrumentation that tries to distinguish thermal kills from other termination types at runtime; the process is dead before any in-process detection can run. The distinction comes from the pattern in urgentry: no minidump, no exception type, abnormal session end, specific device class or playtime range.
FAQ
Does the sentry-unity package capture crashes in IL2CPP builds automatically?
Yes, with a caveat. The package installs the native crash handler for the target platform. Native crashes in IL2CPP-generated code produce a minidump or crash report that arrives at urgentry. Those reports arrive unsymbolicated until you upload the IL2CPP symbol files with sentry-cli debug-files upload for each target platform.
Do I need to run sentry-cli myself, or is there a Unity build integration?
The sentry-unity package includes a Unity Editor build hook that runs sentry-cli automatically after a build when symbol upload is enabled in the Sentry configuration asset. For CI pipelines where the Unity Editor runs headlessly, run sentry-cli debug-files upload --url https://your-urgentry-host as a post-build CI step. Both approaches call the same urgentry debug-files endpoints.
What is the difference between a Blueprint crash and a native crash in Unreal?
Blueprint crashes happen in Unreal's Blueprint VM and produce a logical call stack with graph and node names. No native symbol files are needed to read them. Native crashes in C++ game or engine code produce a minidump with raw addresses that require PDB files (Windows) or dSYM bundles (macOS/iOS) for symbolication. Both crash types reach urgentry through the same Sentry plugin integration.
How does urgentry handle the sentry-cli --url flag for debug file uploads?
urgentry implements all 218 Sentry REST API operations including the full debug-files endpoint suite. Pass --url https://your-urgentry-host to any sentry-cli debug-files upload invocation. The CLI sends the same multipart upload request it sends to Sentry, and urgentry stores the files for the symbolicator to use when resolving crash frames.
Can console platform SDKs point at urgentry?
Yes. Console SDKs that use the Sentry envelope protocol or the minidump endpoint work with urgentry by changing only the DSN or upload URL. The deferred-upload pattern, where crash data is stored on device and uploaded at the next launch, works with urgentry's event ingest the same way it works with Sentry. urgentry stores events with the original crash timestamp from the device.
Sources
- sentry-unity GitHub README — package installation via UPM, the SentryOptions asset, IL2CPP symbol upload configuration, and the Unity Editor initialization order.
- Sentry Unreal plugin documentation — plugin installation, Build.cs configuration,
CrashReportClientintegration, and the DSN configuration in Project Settings. - IL2CPP symbol upload documentation — how to collect and upload IL2CPP symbol files for iOS dSYM, Android unstripped .so, and Windows PDB outputs from Unity builds.
- sentry-cli debug-files documentation — the
debug-files uploadcommand reference, the--urlflag for self-hosted instances, and thechecksubcommand for verifying upload success. - urgentry Sentry API compatibility matrix — the full list of 218/218 covered Sentry REST API operations, including the minidump endpoint and all debug-files endpoints used by the Unity and Unreal SDKs.
- FSL-1.1-Apache-2.0 license text — the license under which urgentry is distributed, converting to Apache 2.0 two years after each release date.
- Unreal Engine CrashReportClient documentation — the CrashReportClient architecture, the .ue4crash file format, and how crash data is collected and posted to a crash reporting endpoint.
- Unity Application.logMessageReceived documentation — the event signature, thread-safety notes, the
logMessageReceivedThreadedvariant for worker threads, and the log type values that correspond to exceptions.
Ready to track game crashes on infrastructure you control?
urgentry runs as a single 52 MB binary, accepts the Sentry SDK from Unity and Unreal without code changes, and handles IL2CPP symbol resolution, Unreal PDB uploads, and console platform minidumps through the same sentry-cli workflow you already know. Create a project, swap the DSN in your Sentry configuration asset or Project Settings, upload your debug files, and your first crash lands in a UI running on your own server.