How to Reproduce an Intermittent Bug: A Practical Guide
To reproduce an intermittent bug, stop treating it as random and start treating it as conditional on an input you haven't controlled yet. Capture the complete state the one time it does fail, list the hidden variables that could differ between runs — timing, data, concurrency, cache state, and environment — then deliberately force each one until the failure becomes reliable. The moment you can trigger it on demand, it's no longer intermittent; it's just a bug with a fix.
Why do intermittent bugs happen at all?
An intermittent bug isn't a coin flip. It fails when a specific combination of conditions lines up, and passes when they don't. The reason it feels random is that the deciding condition is invisible in the normal report: the failure depended on something that wasn't written down.
Almost every intermittent failure traces back to one of five uncontrolled inputs:
- Timing and order — two async operations finish in an order you didn't guarantee, so sometimes B reads state before A has written it.
- Data — a specific value (an empty list, a null field, an emoji in a name, a very large number) hits a path most records never touch.
- Concurrency — two requests, tabs, or workers touch the same resource and race for it.
- Cache and state carry-over — the bug appears only on a cold cache, or only after a previous action left stale state behind.
- Environment — a slower network, a different browser, a specific screen size, or a timezone that shifts a date across a boundary.
Your job isn't to catch the bug in the act again by luck. It's to figure out which of these is the deciding variable, then take control of it.
How do I reproduce an intermittent bug step by step?
Work from evidence outward, isolating one variable at a time so you never confuse yourself with two changes at once.
- Capture everything the first time it fails. You may not see it again for hours, so treat the first occurrence as precious. Record the exact URL and app state, the console log, the network requests and their responses, the timestamp, the browser and OS, the logged-in user, and the input data that was on screen. A right-click, in-app bug report that grabs the screenshot, console, and network trace in one action is worth more here than any written description, because it freezes the state before it's gone.
- Write down what was different this time. Compare the failing occurrence to a normal one. Was the list empty? Was it the first request after login? Was the network slow? Did a background job run? Any difference, however small, is a candidate variable.
- Form one hypothesis. Pick the single most likely deciding variable and state it as a testable claim: "this fails when the cart is empty," or "this fails when the save and the navigation fire within the same tick."
- Force that variable. Make the suspected condition happen every time instead of waiting for it. Seed the exact data. Add an artificial delay to skew the timing. Throttle the network in dev tools. Clear the cache before each run. Open two tabs and act in both.
- Loop it. Run the exact path many times in a row. If your hypothesis is right, the failure rate jumps from occasional to near-constant. If nothing changes, the variable was wrong — revert it and test the next one.
- Lock the trigger. Once it fails reliably, write down the minimal sequence that causes it. That trigger is both your reproduction and your future regression test.
What techniques force a variable to reproduce?
Each of the five inputs has a concrete way to pin it down:
- Timing: insert deliberate delays or breakpoints between the two operations you suspect are racing. If reordering them by hand makes the bug reliable, timing is the cause.
- Data: reproduce with the exact record from production, not a fresh test row. Copy the real payload from the captured network request — the failing value is usually something a hand-typed test would never enter.
- Concurrency: fire the same action twice in quick succession, or run it from two sessions at once. Race conditions that never show up in single-threaded clicking surface immediately under parallel load.
- Cache and carry-over: alternate runs — one on a fresh session, one after performing the prior action — to see whether leftover state is the trigger.
- Environment: throttle the network, switch browsers, resize the viewport, or change the system clock to cross a midnight or DST boundary. Environment bugs vanish the instant you test only on your own fast machine.
The discipline that matters most: change one thing at a time. If you throttle the network and seed new data and open a second tab all at once, a reproduction tells you nothing about which one mattered.
How do I confirm the fix actually worked?
An intermittent bug is uniquely good at looking fixed. If it failed roughly one run in ten, then ten clean runs after your change prove almost nothing — that's exactly what you'd expect by chance even with no fix at all. Confirmation has to be quantitative.
- Reproduce reliably first. You can't verify a fix for a bug you can only trigger sometimes. Getting to a near-100% trigger is the prerequisite, not an optional nicety.
- Run the trigger in a loop, many times, before and after. Establish the failure rate with the fix reverted, apply the fix, and run the same loop again. A drop from frequent failures to zero across dozens of runs is real evidence; a couple of green runs is not.
- Encode the trigger as a regression test. The minimal sequence you isolated is the test. For conditions a script can drive — seeded data, forced timing, throttled network — a self-repairing end-to-end test keeps that guard alive as the UI changes. See how AutoSim turns a reproduction into a durable end-to-end test.
When should I stop chasing and escalate?
Some intermittent bugs resist reproduction because the deciding variable lives on the server, in a third-party service, or in infrastructure you can't see from the client. If you've captured several real occurrences and none share an obvious client-side condition, shift the investigation to logs and traces on the backend rather than clicking more.
This is also where captured evidence pays off twice. When you can't reproduce it yourself, a set of complete reports from different real users — each carrying its own console, network, and state — lets you diff the occurrences and find the shared condition without ever triggering it locally. The bug that's impossible to reproduce on your machine is often obvious across five well-captured reports. That's the case for capturing rich evidence on every report by default, not just the ones you're actively hunting.
For more on capturing state so failures don't slip away, read how to reduce 'cannot reproduce' tickets and how to fix flaky end-to-end tests.
Key takeaways
- Capture full state on the first real occurrence — you may not get it again
- List hidden inputs: timing, data, concurrency, cache, and environment
- Force one variable at a time until the failure becomes reliable
- Re-run in a loop to confirm the fix, not just once
FAQ
Why does an intermittent bug happen sometimes but not always?
Because a variable you aren't controlling changes between runs — the order of async operations, a specific data value, a cache being warm or cold, a race between two requests, or a difference in environment. The bug isn't random; it's conditional on inputs you haven't identified yet.
What's the difference between an intermittent bug and a flaky test?
A flaky test passes and fails without the product changing — the fault is usually in the test (timing, shared state, order). An intermittent bug is a real product defect that only triggers under certain conditions. A flaky test can be hiding a real intermittent bug, so investigate before you add a retry.
How many times should I re-run to confirm a fix worked?
Enough to make the original failure statistically unlikely to have been missed. If the bug reproduced roughly 1 in 10 times before, a handful of runs proves nothing — run it in a loop dozens of times after the fix, ideally with the exact trigger you isolated, before calling it resolved.
Catch bugs the moment a human sees them
Klavity: right-click bug reports, AI personas that review your product, and self-healing tests.
Get started free