Heisenbug

In software, a heisenbug is a bug that changes or disappears when somebody tries to observe it: it shows up in production or under load but vanishes as soon as a debugger is attached, a log line is added or the code is run step by step. The act of looking alters the timing, memory layout or optimisation that the bug depends on.

The longer version

The name borrows loosely from the idea in physics that observing a system disturbs it, and the mechanism is ordinary: the bug depends on something the observation changes. A race between two threads resolves differently once a breakpoint slows one of them. A read of uninitialised memory returns different garbage once a debug build zeroes the stack. A compiler optimisation that reorders two writes is switched off in the debug configuration, and the reordering was the bug. A print statement flushes a buffer that would otherwise have been lost. In each case the defect is real and deterministic given its conditions; the developer's tooling changes the conditions.

A developer conference of the same name exists, and this page is not about it. The nearer neighbours are the ordinary intermittent bug, which happens sometimes for reasons that have nothing to do with observation, and the flaky test, which passes and fails on the same code. A heisenbug is the specific case where the intermittence correlates with being watched. The opposite term of art is the bohrbug, a defect that reproduces reliably under the same conditions and can be found by stepping through it.

The practical failure is that the usual method for finding a bug is the thing that hides it. Reproducing under a debugger is exactly what does not work, and the team falls back on cannot reproduce, which closes the ticket while users keep hitting the fault. What works is observation that does not disturb timing: evidence captured at the moment of failure in the environment where it happened, rather than an attempt to recreate it. Console output, the sequence of network requests, the order of user actions and the state of the page at the time of the report tell the developer which race or which uninitialised value to look for, without triggering it again.

Common questions

Why does a heisenbug disappear when you debug it?

Because the debugger changes the conditions the bug depends on. Breakpoints alter thread timing, debug builds initialise memory that release builds leave as garbage, optimisations that reorder operations are switched off, and added logging flushes buffers. The bug is deterministic given its conditions; the tooling changes them.

How do you fix a heisenbug?

Capture evidence in the environment where it happens instead of trying to recreate it under a debugger: logs written to a buffer and flushed later, timestamps, the order of requests, the page state at the moment of failure. Then look for the usual causes in the code path involved: races, uninitialised memory, undefined behaviour and reliance on evaluation order.

What is the difference between a heisenbug and a flaky test?

A flaky test passes and fails on the same code without observation being involved, usually because of timing, shared state or an external dependency. A heisenbug is a defect whose behaviour changes specifically when someone tries to observe it. A flaky test can be caused by a heisenbug, but most flakiness has a duller cause.

What is the opposite of a heisenbug?

A bohrbug: a defect that reproduces reliably whenever its conditions are met and holds still under a debugger, so stepping through the code finds it. Most bugs are bohrbugs. The distinction matters at triage, because a bohrbug is handed to a developer with the steps, while a heisenbug needs evidence captured at the moment of failure before anyone opens a debugger.

What causes a heisenbug in a web application?

Races between asynchronous requests are the usual cause: two responses arriving in an order the code did not expect, a render that reads state before a fetch resolves, a timer that fires late on a slow device. Opening the browser's developer tools changes timing and sometimes layout, which is why the fault vanishes when a developer looks. Caching and third-party scripts that load in variable order supply the rest.

Bug reports that arrive ready to triage.

Collect replay, console, network and browser context with the Free plan.

<script src="…/widget.js" data-key="pb_pub_…">