"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.

SignalWithout itWith it
Video before the failureReconstruct steps from memoryWatch what actually happened
Logs at failure timeReproduce later to collect againExceptions 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.