Quick answer: Make a development build with script debugging enabled, attach the profiler and debugger to the running player, and diff the build-only code paths (stripping, scripting defines, asset bundles) against the editor.
When a bug appears only in a build, you have lost the editor's inspector and breakpoints, so the instinct is to guess. Don't. The build is a different runtime, and you can attach the same tools to it. The trick is making a build that is debuggable in the first place.
How to track it down
1. Build a debuggable player
Under Build Settings enable Development Build and Script Debugging (and Wait For Managed Debugger if it crashes early). This gives you the in-build console, profiler connection, and breakpoints that a release build strips out.
2. Attach the profiler and debugger to the running build
In the Profiler window switch the target from Editor to your running player on the network, and in your IDE choose the player as the debug target. Now breakpoints and the call stack work against the actual build, not a re-run in the editor.
3. Read the player log
The build writes everything to Player.log (%LOCALAPPDATA%Low\Company\Game\ on Windows, ~/Library/Logs on macOS). Exceptions swallowed silently in a build still land here with a stack trace.
4. Diff the build-only paths
Compare scripting define symbols, managed stripping level, and addressable/bundle loading between editor and player. A static field that survives a domain reload in the editor, or an asset loaded from a different path, is the usual culprit.
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.
The bug you can't reproduce isn't gone — it's just invisible until you capture it from the player's device.