Quick answer: A game that freezes, becomes unresponsive without closing, is hanging rather than crashing. The main loop has stopped advancing, typically because of an infinite loop (code that never exits), a deadlock (two things each waiting for the other), or the main thread blocked on something slow like a stuck I/O or network call. Because no crash occurs, you need to detect the hang and capture what the main thread was doing to find the cause.
A freeze is in some ways worse than a crash: the game doesn't even close, it just stops, frozen on a static frame, ignoring input, forcing the player to kill it. And because freezes produce no crash and no error by default, they can be invisible to crash-focused monitoring even as players hit them. Fixing random freezes starts with understanding that the game is stuck, not dead, and finding what it's stuck on.
What Causes Freezes
A freeze means the game loop stopped making progress while the process is still alive. The causes: an infinite loop, code stuck repeating forever because an exit condition is never met, so it never returns to render or process input. A deadlock, two parts of the program each waiting on the other (often around threads and locks), so neither can proceed. Or a blocked main thread, the main loop waiting on something slow or stuck, a hung network request, a synchronous file operation, a lock, so it can't advance.
'Random' freezes usually aren't truly random, they depend on conditions (a specific action, timing, or state) that aren't obvious. A freeze that happens during a particular operation points at that operation blocking; one tied to multithreading points at a deadlock or race; one that's timing-dependent may be a rare loop condition. The randomness is unidentified conditions, not true chance.
How to Diagnose a Freeze
Since a freeze produces no automatic crash, you need a way to detect it and capture the state. The key technique is a watchdog: detect that the game loop hasn't advanced for too long, treat it as a hang, and capture the main thread's stack trace, what it's stuck doing, which is the single most important clue. The frozen main thread's trace usually points right at the infinite loop, the lock it's waiting on, or the slow call it's blocked in.
This is conceptually how ANR (Application Not Responding) detection works on mobile, and the same idea applies everywhere. Capturing the hang's context, especially the main thread state, alongside your crashes means freezes are tracked rather than invisible. Bugnet captures context and state around problems, so a detected hang arrives with the information to identify what the game was stuck on, turning an invisible freeze into a diagnosable issue.
How to Fix It
Once the main thread's stuck state points at the cause, fix accordingly. For an infinite loop, find why the exit condition is never met (a counter that never reaches its target, a state that never changes) and ensure the loop can always terminate. For a deadlock, fix the lock ordering or the mutual wait so the two parties can't block each other, often by acquiring locks in a consistent order or avoiding holding one while waiting for another. For a blocked main thread, move the slow operation (file I/O, network, heavy computation) off the main thread so it can't freeze the game, and add timeouts so a stuck call doesn't hang forever.
The general principle: the main thread must always keep advancing, so nothing slow or potentially-stuck should run on it synchronously, and no loop on it should be able to run forever. After fixing, verify the freeze stops in the field, and keep hang detection in place so any new freeze is caught and tracked rather than silently frustrating players.
A freeze is the game stuck, not dead, an infinite loop, a deadlock, or a blocked main thread. It throws no error, so detect the hang and capture what the main thread is stuck on.