Quick answer: Record every client input and every server snapshot tick to a replayable log, and when a player reports a missed shot, replay those logs against the actual server code. Lag compensation bugs cannot be reproduced from screenshots or verbal descriptions — you need the exact tick history.

A player fires a shot that they clearly saw hit an enemy in their crosshair. The server says they missed. The player posts a clip with "hitreg is broken" in the title and it gets 800 upvotes. You try to reproduce locally and the game works perfectly. This is the most frustrating class of multiplayer bug and also the most common one in shooters, fighting games, and any game with hit detection across latency. Debugging it requires a specific workflow: you have to capture exactly what the server saw, then replay it.

A Brief Lag Compensation Refresher

In a lag-compensated game, the client sends an input like "I fired my gun at tick 1000 pointing at angle (45, 20)". The server receives this at, say, tick 1008 — the client is 80 ms behind the server clock due to network latency. Without lag compensation, the server would check hitboxes at tick 1008, where the target has moved, and the shot misses. With lag compensation, the server rewinds the world state to tick 1000, performs the hit check, and then unwinds time forward again.

This works beautifully when everyone is on ping under 50 ms. It falls apart when:

Step 1: Record Everything

Lag comp bugs are impossible to debug without a full recording. On the server, log every input received from every client with its tick and a wall-clock timestamp, and log every snapshot you send out. The format is append-only binary — you will write a lot of it.

type RecordedInput struct {
    PlayerID  uint32
    ServerTick uint32  // server tick when received
    ClientTick uint32  // tick client claims input happened on
    WallClock int64   // nanoseconds, for correlating with logs
    Buttons   uint32
    AimYaw    float32
    AimPitch  float32
}

type RecordedSnapshot struct {
    ServerTick uint32
    Players    []PlayerState  // positions, orientations, hitboxes
}

func (r *Recorder) RecordInput(inp RecordedInput) {
    binary.Write(r.inputFile, binary.LittleEndian, inp)
}

Store the recording per match, indexed by match ID. A 10-minute match of 10 players at 60 Hz is around 50 MB of binary data — cheap to store, invaluable to debug.

Step 2: Correlate With Reports

When a player submits a bug report, they need to provide enough information for you to find the match in the recording storage. Include in every in-game bug report form:

If you can attach a short video clip (last 10 seconds) that is even better — it gives you a visual ground truth for what the player saw.

Step 3: Build a Replay Tool

The replay tool loads the recorded inputs and snapshots for a specific match and walks through them tick by tick against the actual game logic. At any given tick you should be able to see:

This is the critical piece. Build a simple 3D viewer (or 2D, depending on your game) that overlays:

When the player's report says "I clearly hit them", you load the replay to the reported tick and look at where these boxes are. Seven times out of ten, the orange rewound box is not where the player thought the target was. Once you can see the discrepancy, the fix usually becomes obvious.

Step 4: Diagnose the Common Causes

Rewind time out of range. Your snapshot history might be a 1-second ring buffer (60 ticks at 60 Hz). A player with 300 ms ping plus 200 ms of jitter might request a rewind to 500 ms ago, which is outside the buffer. The server snaps to the oldest tick in the buffer, which does not match the player's view. Fix: extend the buffer to 2 seconds minimum.

Client tick clamping. Some anti-cheat logic clamps the client's reported tick to a maximum acceptable offset from the server. If you clamp too aggressively, legitimate shots from high-ping players get compensated against the wrong tick. Log every clamp event and check whether your threshold is too tight.

Hitbox interpolation offset. If your client renders the enemy interpolated 100 ms behind the current client time (for smooth motion), but your server rewinds to the client's current tick, you have a 100 ms offset between what the player saw and what the server tested. The server should rewind to client_tick - interpolation_offset, not just client_tick.

Composite hitboxes. A character made of a torso, head, and limbs may have each part tracked as a separate body. If any of them are not included in the rewind, you get impossible hits (or impossible misses).

Ping estimate lag. Some games use an exponential moving average of round-trip time as the compensation amount. If ping suddenly spikes, the moving average lags behind and compensation is wrong for several seconds. Use a more responsive estimate (median over last 10 packets) or compute compensation per-input from the actual tick delta.

Step 5: Add Sanity Check Metrics

Instrument every hit test with metrics that catch anomalies in aggregate:

metrics.Histogram("hit_rewind_ms", rewindMs)
metrics.Histogram("hit_result", hit, map[string]string{
    "weapon": weapon,
    "distance_bucket": distanceBucket,
})
metrics.Counter("hit_rewind_buffer_miss").Inc()

Plot these in your dashboard and watch for:

Step 6: Close the Loop With Players

When you identify a specific report as "worked as intended" or "bug confirmed", reply to the reporting player with a clip or diagram from your replay tool. Players are much more forgiving when they can see exactly what happened — "You aimed here, the target was actually here, your ping was 180 ms and our server rewound to 150 ms ago" — than when they get "not reproducible" as a closing note.

"The best multiplayer networking team I ever worked on had a replay tool that could load any reported shot and overlay it on the server's view. It was the only thing that let us ship lag compensation without constant player rage."

Related Issues

For general multiplayer desync debugging see how to debug multiplayer desync issues. For network packet loss that often compounds lag compensation problems, read how to debug network packet loss.

Record everything. You cannot debug what you did not capture.