Someone reports a failure. You follow their steps on your machine and everything works. The honest summary of what just happened is: two runs of the same procedure produced different results.

That is a finding. It is not the finding people usually record, which is “no bug.”

What the Failed Attempt Actually Measured

You didn’t test whether the bug exists. You tested whether the bug survives translation into your environment. Those are different questions, and the second one has an enormous number of confounds — different data, different clock, different account, different concurrency, different version of something three layers down that neither of you thinks about.

The result you got narrows the search. Every variable you held constant and still saw success is a variable that isn’t sufficient on its own to cause the failure. That’s real information. It’s the beginning of a bisection, not the end of an investigation.

Treating it as a verdict throws that information away and adds a false one in its place.

The Asymmetry Nobody Accounts For

A reproduction is proof. A failed reproduction is absence of proof, and those carry wildly different weight — but organizationally they get filed identically, as one attempt each.

The person who reported it saw the failure happen. Their evidence is direct observation. Yours is a single negative trial under conditions you chose, with the specific goal of matching an environment you don’t have access to and probably can’t fully describe. Against a real observation, one clean run is not much.

This asymmetry gets inverted constantly, because the person running the reproduction is usually the one with authority to close the ticket.

What to Do With the Negative Result

The useful move is to write down what you did hold constant. Not as ceremony — as the actual output of the attempt. “Ran the reported steps on the current build, single user, empty cache, default region, no concurrent writes: success.” That sentence is worth more than a status change, because the next person who picks this up can start by varying one of those.

Then ask the reporter for the one or two variables most likely to matter, rather than for “more detail.” More detail is unbounded and mostly produces noise. A specific question — what region, was anything else running, how large was the input — is answerable in a sentence and rules in or out a whole branch.

If it still won’t reproduce after a few rounds, the correct state isn’t closed. It’s open with a shorter list of candidate causes than you started with, and a note about what you already eliminated.

The Real Cost of Closing It

A bug closed as unreproducible doesn’t stop happening. It stops being reported, which is worse, because the next person who hits it finds a closed ticket describing their exact symptom and concludes the problem is on their end.

You haven’t fixed anything. You’ve just moved the failure somewhere you can no longer see it.