Quick answer: Rollback netcode crashes are uniquely hard because the fault often fires during resimulation, many frames removed from the input that caused it, and a desync can corrupt state long before anything throws. The fix is to capture the frame number, the input history being replayed, and a state checksum at the crash, so you can reconstruct the exact rollback that triggered the fault and replay it deterministically.

Rollback netcode makes online fighting and action games feel local by predicting remote inputs, then quietly resimulating past frames when the real inputs arrive. It is wonderful for players and brutal for debugging. A crash that surfaces during resimulation may have nothing to do with the frame you are on; the corrupting input could be eight frames in the past, replayed under conditions you never see in single-player. Add desyncs, where two clients silently diverge, and you have a class of bugs that ordinary crash reporting barely touches. This post covers how to capture rollback crashes with the frame, input, and checksum context that actually lets you reproduce them.

The crash is not where the bug is

Rollback works by saving game state at a confirmed frame, predicting inputs forward, and then, when authoritative inputs arrive, rolling back to the saved state and resimulating with the corrected inputs. That resimulation runs your game logic again, sometimes several times per frame, on inputs that differ from what was originally simulated. A crash during resimulation therefore happens under a state combination that never occurred during the live, forward simulation, which is exactly why it never reproduced when you played offline.

This decoupling between where the crash surfaces and where the bug lives is the central debugging problem. A null reference thrown on frame 1200 might be caused by an input on frame 1192 that, when replayed in a slightly different order, drove an entity into a state your code did not expect. A plain stack trace gives you frame 1200 and nothing about the eight-frame rollback that set it up, leaving you to guess at a window you cannot see.

Capture the rollback window, not just the frame

To make a rollback crash reproducible, capture the rollback window around it. Record the current frame, the frame you rolled back to, and the input history for every player across that span. Rollback systems are deterministic by design, so a saved state at the rollback frame plus the exact input sequence forward is, in principle, all you need to replay the crash byte for byte. Attaching that sequence transforms an irreproducible online crash into a deterministic offline repro.

Be selective about size. You do not need the whole match, only the confirmed state at the rollback frame and the inputs from there to the crash, which is usually a handful of frames. Keep input encoding compact, a few bytes per player per frame, so the report stays small. The discipline that makes rollback netcode possible, strict determinism and small inputs, is the same discipline that makes its crashes capturable, so lean on it.

Desyncs are crashes waiting to happen

A desync, where two clients simulate different states from the same inputs, is the signature failure of rollback netcode, and it often precedes a crash. The moment determinism breaks, perhaps from an uninitialized variable, a floating-point difference, or iteration over an unordered collection, the two clients drift, and one of them eventually hits a state the other never will. Catching the desync at the moment of divergence is far more useful than catching the crash it eventually causes.

The standard tool is a per-frame state checksum exchanged between clients. When checksums diverge, you have a desync, and you can report it immediately with the frame number and the inputs leading up to it, before any crash. Capturing the last matching frame and the first diverging frame brackets the bug tightly. A desync report is essentially an early-warning crash report, and treating it as reportable in its own right catches determinism bugs while they are still cheap to fix.

Determinism is the thing to protect

Most rollback crashes and desyncs trace back to a determinism violation, so your crash context should make those violations visible. Attach the platform and architecture, because floating-point and ordering differences across platforms are a classic desync source. Note the simulation tick rate and any fixed-point or float configuration. When a desync report only ever pairs one platform against another, you have found a cross-platform determinism bug without ever reproducing it manually.

Beyond reporting, the lesson is preventive: anything nondeterministic, wall-clock time, unordered iteration, uncontrolled random, has no place in your simulation step, and your crash data will keep pointing at whatever slipped through. Treat each desync signature as evidence of a determinism leak and hunt the source rather than papering over the symptom. The cleaner your determinism, the fewer of these reports you get, which is the best outcome of taking them seriously.

Setting it up with Bugnet

Bugnet captures rollback crashes with the same automatic stack trace and platform context as any game, but the leverage is in the custom fields you add for this domain. Attach the current frame, the rollback target frame, the compact input history across the window, and the state checksum at the crash. Wire your desync detector to file a report the instant checksums diverge, with the last matching and first diverging frames. Now both crashes and their precursor desyncs land in one dashboard with the rollback window intact.

Occurrence grouping is especially valuable here because rollback bugs recur under specific input patterns. Identical crashes fold into one issue with a count, so a determinism bug triggered by a particular move sequence reads as a single climbing signature rather than scattered, irreproducible reports. Filter by platform pairing to isolate cross-platform desyncs, or by frame window to spot a state-save bug. The attached inputs mean many of these crashes replay deterministically on the first local attempt.

Replay-driven testing

The payoff of capturing the rollback window is replay-driven testing. Feed a crash report's saved state and input sequence into your simulation offline and it should reproduce the fault exactly, because the system is deterministic. Turn the worst recurring reports into regression fixtures: a state and an input log that must simulate cleanly. Once a determinism bug has a replay, it stops being a mystery and becomes a unit test, which is the strongest position you can be in.

Build a culture of treating desync reports as seriously as crashes, because in rollback netcode a desync is just a crash that has not happened yet. The teams that win at online action games are the ones who made their nondeterminism observable and their crashes replayable, so a fault that surfaces eight frames late on one player's machine still ends up as a precise, repeatable repro on theirs.

In rollback netcode the crash is rarely where the bug is. Capture the input window and the checksum, and a buried fault becomes a deterministic replay.