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
| State | Example | Why |
|---|---|---|
| Location | Scene, level or room; player position and facing | Lets you teleport there in a debug build |
| Progress | Current quest or objective, checkpoint, key flags | Many bugs only exist in one progress state |
| Character | Level, health, equipped items, active effects | Combinations testers never tried |
| Randomness | Run seed, procedural level seed | Regenerates the exact layout |
| Settings | Difficulty, graphics preset, resolution, language, input device | Settings-specific bugs are common and invisible otherwise |
| Session | Play time this session, time since last load | Leaks and timing bugs show up late |
| Recent actions | The last 20 meaningful events (opened inventory, used item) | The steps to reproduce, without asking |
| Multiplayer | Host or client, player count, latency | Network 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:
UnityBugnetSDK.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
- No personal data. No account emails, chat messages or real names in the snapshot.
- Small. A few kilobytes. Attachments are limited to 10 MB and count toward your plan's upload limit.
- Versioned. Add a schema version so old snapshots still load after you change the format.
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.