How to capture the state before a crash

A stack trace only says where the game died. The logMessageReceived hook, the limits of the quitting event, watchdog processes, and periodic snapshots — four ways to keep the state leading up to a crash, and what each one costs.

  • unity
  • crash
  • logging

Say you've got the crash log and you know what the crash handler does and doesn't catch. One thing is still missing. A stack trace says where the game died. It doesn't say why it got there.

What a stack trace doesn't say

It'll tell you which line threw the NullReferenceException. It won't tell you why that reference was null, what scene transition happened right before, or how many items were in the inventory. A crash report gives you the location of the event, not how you got there. Reconstructing that takes information from the window before the crash, and that has to come from somewhere else.

logMessageReceived — the easiest starting point

The first thing most people reach for is hooking the log callback and keeping recent logs in a ring buffer. Unity's Application.logMessageReceived fires only on the main thread when a log message is received. That makes it useful for small buffer updates that need the Unity API, but it does not promise to run after a native crash or wait for disk writes to finish.

private bool snapshotRequested;
private float nextWriteAt;

void OnEnable()
{
    Application.logMessageReceived += HandleLog;
}

void OnDisable()
{
    Application.logMessageReceived -= HandleLog;
}

void HandleLog(string condition, string stackTrace, LogType type)
{
    ringBuffer.Push($"[{type}] {condition}");
    snapshotRequested = true;
}

void LateUpdate()
{
    if (!snapshotRequested || Time.unscaledTime < nextWriteAt) return;

    snapshotRequested = false;
    nextWriteAt = Time.unscaledTime + 1f;
    ScheduleSnapshotWrite(ringBuffer.Copy()); // Pass a copy with no Unity objects.
}

This is a conceptual example. ScheduleSnapshotWrite is not a Unity API; it stands in for a safe write of a bounded copy that contains no Unity objects. Calling FlushToFile directly from a log callback can block the main thread on file I/O, and a log emitted during that write can re-enter the callback. More importantly, a ring buffer that exists only in memory dies with the process. To have a candidate left after a hard crash, persist a copy at a short interval outside the callback, not only after an error or exception. In production, also bound the buffer and make sure a failed write cannot recursively emit Debug.Log. The ceiling of this approach remains the same — it only reconstructs what got logged. Whatever state changed without a log line simply isn't information you have.

The trap in quitting and OnApplicationPause

It's tempting to put snapshot-saving logic in Application.quitting or OnApplicationPause, on the theory that you're saving state right before shutdown. Application.quitting notifies you when the Player quits, but Unity explicitly says it is not raised for a forced quit or a crash. OnApplicationPause receives pause-state changes; it is not a crash-detection hook. Both can add one more saved state on a clean exit or pause, but neither guarantees a completed write during a native crash or OS termination.

Watchdogs — why the watcher has to live outside the process

An observer outside the process that dies is one option for reacting to a hard crash. This would preserve the team's game-state snapshots separately from the crash artifacts discussed in the Unity Crash Handler post. Built by hand, it becomes a separate process that checks whether the game process is alive and preserves the last snapshot that was already written safely alongside the crash report. A watchdog still has to account for platform policy, permissions, and false positives during a long stall; it cannot recover new state from inside a dead process. Test a combination that fits the platform, including platform crash reporting or external monitoring where appropriate.

Periodic snapshots — cost and compromise

Another option is writing game state — scene name, recent logs, key variables — at a fixed interval. It stores the same kind of thing covered in game state snapshots, but the goal here is different: not "diagnose the current state" but "reconstruct the state right before death." A shorter interval gets you closer to the crash moment at the cost of more frequent disk writes; a longer one trades the other way. Reusing one file can bound disk use, but a process that dies mid-write can leave that file truncated or corrupt. Where the file system supports it, write a temporary file then replace atomically, and retain the previous known-good snapshot. Even then, you have only the last state that was successfully written.

Why a window, not a moment

All four approaches point at the same conclusion. A single snapshot taken at the moment of the crash isn't enough. It's the same reasoning behind the rolling buffer approach — the cause is usually somewhere before the crash, not at the crash itself, and what you need isn't a photo at t-0 but footage of the t-30s window. Whether it's a log ring buffer or a periodic snapshot, everything converges on persisting the preceding window at short intervals before a crash. When a person observes an anomaly, a hotkey can commit that window; a hard crash itself does not provide that commit.

Summary

ApproachWhat it capturesUnder a real crash
logMessageReceived ring bufferWhatever got loggedOnly pre-persisted, pre-crash entries remain; no flush guarantee
quitting / OnApplicationPauseClean exit, pauseDoes not guarantee crash detection or a completed write
Watchdog processProcess liveness and existing filesPlatform, permission, and false-positive behaviour need testing
Periodic snapshot fileUp to the last successful writeConsider atomic replacement and keeping a previous file

Whichever one you pick, the principle is the same. Deciding what to keep after a crash happens is already too late. Something has to be accumulating before the crash ever occurs.

At Rekon, a rolling buffer keeps roughly the last 60 seconds of video, logs, and performance data during Play Mode. When QA sees something wrong and presses the hotkey, that window is captured and game state such as the scene is attached at the time of that hotkey capture. This is a workflow for preserving evidence of an observed anomaly; it does not automatically save or submit a report on a hard crash. If hard crashes matter to your project, test the right combination from this post against the platform and your actual crash frequency.