"Cannot reproduce": what your Unity bug ticket is missing
The three reasons reproduction fails, what a bug ticket actually has to carry, and why writing it down by hand is structurally too late.
- qa
- unity
- bug-report
Unity bug reporting, part 1 of 3 — ① Why reproduction fails · ② Always record, keep what you need · ③ A minimal Unity setup (②·③ publishing weekly)
QA finds a bug. Writes a ticket. A developer picks it up, walks the same steps, and nothing happens. A few days later the ticket closes as "cannot reproduce".
Weeks later, the same bug shows up in production.
That scene is where our Unity bug reporting tool started. The problem wasn't that QA had made it up, or that the developer was careless. Nothing survived the moment the bug occurred.
Three places reproduction breaks down
"Cannot reproduce" looks like one outcome, but it usually traces back to one of three causes.
1. The conditions are gone
Here's what the ticket says.
The inventory UI broke while I was moving an item.
The developer opens the inventory and moves an item. It's fine. The conditions that actually mattered were these:
- the inventory was completely full
- a quest reward had just auto-added an item
- the target slot held a currently equipped item
QA never registered those three as conditions. It was just how the session was going. The moment you translate a repro into prose, the context that felt obvious to you drops out.
2. The timing doesn't come back
Frame-level races, network response ordering, coroutine completion — bugs that depend on time won't reappear just because you followed the steps. If the developer's machine is faster, the window never opens.
These need a timeline of that moment, not a procedure. Which logs fired in what order, and where the frame time spiked.
3. The environment differs
The same build behaves differently across graphics APIs, resolutions, quality settings, OS versions — even windowed versus fullscreen. Tickets usually have a field for this, and it's usually empty or just says "Windows".
Two more belong here. QA and the developer may not be on the same build, and the bug may depend on server state or a live-ops event. Neither shows up if you only record client settings. Capturing the build number, the server you connected to, and which events were active is what surfaces them.
So what should a ticket carry?
The difference between a reproducible ticket and a dead one isn't diligence. It's what was left behind.
| Signal | Without it | With it |
|---|---|---|
| Video before the failure | Reconstruct steps from memory | Watch what actually happened |
| Logs at failure time | Reproduce later to collect again | Exceptions and warnings in order |
| Game state | "In the inventory, I think" | Scene, items, quest progress |
| Performance | "It felt laggy" | The real spike in FPS and memory |
| Environment | "Windows" | Resolution, graphics API, quality, build |
What matters here isn't the list of rows. It's that the middle column is entirely "reconstruct it later" — and reconstruction is exactly the step that fails, taking the ticket with it.
Lay it over the three causes and the mapping is clean. Lost conditions are covered by game state, unreproducible timing by logs and performance metrics, environment drift by build and settings data. None of it is the kind of thing memory reconstructs after the fact.
Why writing it down by hand doesn't stick
"Then QA should just record all of this" sounds reasonable and rarely survives contact with a real sprint. There's a structural reason.
You notice a bug only after it has already happened. The record button is always pressed too late.
[t-30s] Quest reward claimed ← the actual cause
[t-12s] Opened inventory
[t-3s] Dragged an item
[t=0] UI breaks ← "wait, that's a bug"
[t+8s] Recording starts ← the cause is already behind you
You need the 30 seconds before, and nobody is recording then. Worse, anything written after the fact has already passed through memory. "The inventory was full" wasn't omitted from the ticket so much as never classified as a condition in the first place.
Solving that with discipline would mean recording every play session, always. That isn't a discipline problem. It's a tooling problem.
If tickets keep closing as cannot reproduce, it may not be that your QA process is sloppy. More likely, there was never a way to keep the evidence in the first place.
So how do you actually run "always recording" without paying for it in frame time and disk? The next post covers the rolling buffer approach.