Quick answer: Reproduce with a development IL2CPP build to get the real log, preserve reflected types with a link.xml, and avoid AOT-unfriendly generics — then check the native crash log for the stripped or missing type.
A crash that only appears in an IL2CPP build is a build-pipeline difference, not your gameplay logic. Mono is forgiving where IL2CPP is strict. Here is how to surface what IL2CPP removed or could not compile.
How to fix it
1. Build a development IL2CPP build and read its log
Enable Development Build and Script Debugging, then read the device log (logcat or the Xcode console). IL2CPP crashes usually log a managed stack or a “type not found” that names exactly what was stripped.
2. Preserve reflected and serialized types with link.xml
Code stripping removes types it cannot see referenced — common with JSON deserialization and reflection. Add a link.xml that preserves those assemblies or types so IL2CPP keeps them.
3. Avoid AOT-unfriendly generics
IL2CPP must generate concrete code ahead of time; some generic and value-type combinations are never generated and throw at runtime. Reference the concrete instantiations you need so they are compiled in.
Catching the ones you can't reproduce
The hardest version of this to fix is the one you can't reproduce — it only happens on a player's hardware, OS, driver, or save state, under conditions that simply aren't present on your machine. A report that says “it crashed” or “it froze” gives you nothing to act on, so the bug survives release after release while quietly costing you players.
Automatic error capture closes that gap. Each failure arrives with its full stack trace, the device and OS, the build number, and a breadcrumb trail of what the player did right before it broke, so even a failure you have never seen becomes a specific, reproducible issue. Fold identical failures into one signature ranked by how many players each hits, and your worklist sorts itself worst-first instead of arriving as a stream of vague complaints.
This is where a tool like Bugnet earns its place. Its SDK captures every Unity error automatically with the full stack trace plus device, OS, memory, build, and game-state context, folds duplicates into one grouped issue with an occurrence count, and ties each to the build it first appeared on — so you fix the problem that hurts the most players first and confirm it is gone when its signature disappears from the next release.
Ship the fix, watch the signature disappear from the next build. That's how you know it's really gone.