Quick answer: Capture a compact snapshot of the game when a report is filed: scene or level, player position, current objective, difficulty and settings, play time, RNG seed and the last few player actions. Attach it as a JSON file to every report from the SDK's report callback, and log a one-line summary on each scene change so automatically captured errors carry it too. Keep it small, and never include personal data.

Most “cannot reproduce” bugs are not mysterious. The player was on a different level, with a different item, on a different difficulty, with a seed you will never roll again. The fix is to have the game describe its own state whenever someone reports a bug, so you can put yourself exactly where they were.

What to capture

StateExampleWhy
LocationScene, level or room; player position and facingLets you teleport there in a debug build
ProgressCurrent quest or objective, checkpoint, key flagsMany bugs only exist in one progress state
CharacterLevel, health, equipped items, active effectsCombinations testers never tried
RandomnessRun seed, procedural level seedRegenerates the exact layout
SettingsDifficulty, graphics preset, resolution, language, input deviceSettings-specific bugs are common and invisible otherwise
SessionPlay time this session, time since last loadLeaks and timing bugs show up late
Recent actionsThe last 20 meaningful events (opened inventory, used item)The steps to reproduce, without asking
MultiplayerHost or client, player count, latencyNetwork bugs depend on role and lag

Bugnet's SDKs already record the platform, OS, hardware, game version and a screenshot, so you only need the game-specific part.

Build a snapshot

Keep a single function that serialises the state to a small JSON object, a few kilobytes at most:

Unity
[System.Serializable] class StateSnapshot {
    public string scene, objective, difficulty; public Vector3 position;
    public int seed; public float playTime; public string[] recentActions;
}

string CaptureState() => JsonUtility.ToJson(new StateSnapshot {
    scene = SceneManager.GetActiveScene().name, position = player.transform.position,
    objective = quests.CurrentId, difficulty = settings.Difficulty.ToString(),
    seed = run.Seed, playTime = Time.realtimeSinceStartup, recentActions = actionLog.ToArray()
});

Attach it to every report

The SDK's report callback gives you the new report's id, for reports players file from the widget and for automatically captured errors alike. Upload the snapshot as a JSON attachment; it appears under the report's Attachments, ready to download:

Unity
BugnetSDK.Instance.BugReported += (bugId, reportNumber, title) =>
    BugnetSDK.Instance.UploadAttachmentBytes(bugId, "state.json", Encoding.UTF8.GetBytes(CaptureState()), "application/json");
Godot 4
Bugnet.bug_reported_id.connect(func(bug_id, _number, _title):
    Bugnet.upload_attachment_bytes(bug_id, "state.json", JSON.stringify(capture_state()).to_utf8_buffer(), "application/json"))
Web
Bugnet.init({ apiKey: 'YOUR_API_KEY', serverUrl: 'https://api.bugnet.io',
  onBugReported: (bugId) => Bugnet.uploadAttachmentBytes(bugId, 'state.json',
    new TextEncoder().encode(JSON.stringify(captureState())), 'application/json') });

Pygame works the same way with bugnet_sdk.set_on_bug_reported() and upload_attachment_bytes(), and Unreal with the OnBugReportedId event and UploadAttachmentBytes(). The callback also fires when a report stacks onto an existing bug, which is useful here: each occurrence gets its own snapshot, so you can compare the states of everyone who hit the same crash.

For a handful of fields on reports players file themselves, you can skip the attachment: pass the snapshot as the metadata argument of ReportBug() / reportBug() (up to 16 KB of JSON) and it appears as a Custom Data table on the bug page, readable at a glance. Use the attachment for anything larger, and for automatically captured errors.

Log a one-line summary too

Automatically captured errors include the last lines of your game's log, so a short line on every scene change or checkpoint puts the essentials right in the report text, before anyone opens an attachment:

[STATE] scene=Forest_02 objective=find_key difficulty=hard seed=48213

Turn the snapshot into a repro

The snapshot pays off when your debug build can load it: a console command or menu that reads state.json, loads the scene, moves the player, sets the flags and seeds the RNG. Combined with a player's save file, most reports become reproducible in minutes.

Keep it lean and private

Create a free Bugnet project to attach game state to every report. For what a complete report looks like, see how to write a good bug report.

Frequently asked questions

What game state should a bug report include?

The scene or level, player position, current objective and key progress flags, character state, RNG seed, settings such as difficulty and graphics preset, play time and the last few player actions.

How do I attach game state to a Bugnet report?

Listen for the SDK's report callback, which gives you the new report's id, and upload a JSON snapshot with the SDK's attachment upload function. It appears under the report's attachments.

Does it work for automatically captured crashes?

Yes. The report callback fires for automatically captured errors as well as player reports, and a one-line state summary in your log also arrives with the error's recent log lines.

How big should a state snapshot be?

A few kilobytes. Attachments can be up to 10 MB, but they count toward your plan's upload limit, and small snapshots are easier to read.

Should game state include player data?

Only game data. Leave out emails, chat logs, real names and anything else personal.

Every report should let you stand exactly where the player was standing when it went wrong.