How to Reproduce a Production-Only Bug That Never Happens Locally
A bug that only appears in production almost always traces to a parity gap: your local environment has different data, configuration, scale, integrations, or user state than production, and the bug lives inside that difference. To reproduce it, capture the exact production request, environment, and account state at the moment it failed, then recreate that one variable at a time locally until the bug appears. The failure is deterministic — you just need to feed your code the inputs production gave it.
Why does a bug only happen in production?
Your code runs the same everywhere. What changes between your laptop and production is the world the code runs in. A production-only bug is your code behaving correctly given an input you never generate locally — a null field a real customer left blank, a feature flag that's on for one account, a payload ten times larger than your seed data. Chasing the code without changing the environment is why these bugs eat days.
The useful mental shift: don't ask "what's wrong with the code?" Ask "what does production have that my machine doesn't?" That reframes an open-ended debugging session into a short checklist.
The five parity gaps that cause production-only bugs
Almost every production-only bug hides in one of these five differences. Work through them in order — the earlier ones are more common and cheaper to check.
- Data shape. Real records have nulls, emojis, very long strings, unusual timezones, legacy formats, and volumes your seed data doesn't. A function that works on your three clean test rows can crash on the customer's account with 40,000 orders and one malformed address.
- Config and feature flags. Environment variables, feature-flag states, and per-tenant settings differ between local, staging, and production. A bug that only hits one customer is often a flag that's on for them and off for you.
- Scale and concurrency. Pagination limits, timeouts, connection-pool exhaustion, and race conditions only surface under production load or with production-sized datasets. These look intermittent but are usually deterministic once you match the volume.
- Third-party integrations. Payment providers, auth services, and external APIs behave differently in production than in the sandbox or mock you run locally — different rate limits, response shapes, latency, and error codes.
- User and permission state. The bug may only exist for a specific role, plan tier, onboarding state, or account age. If you always test as an admin on a fresh account, you'll never see what a read-only user on a two-year-old account sees.
How to reproduce a production-only bug, step by step
Once you know the parity gaps, reproduction becomes a disciplined narrowing process rather than a hunt.
- Capture the failing moment first. Get the exact URL, the request and response, the console errors, the environment (browser, OS, app version), and which account hit it. Reproduction is impossible without knowing the real inputs — and this is exactly the state a good in-app bug report should carry, so you're not reconstructing it from memory.
- Pin down the account and its state. Identify the specific user, role, plan, and data that triggered it. "It happens for this customer" is a much smaller search space than "it happens in production."
- Reproduce against a production-like environment. Before touching your laptop, try to trigger it in staging with production-shaped data, or against a copy of the affected account. If it reproduces there, you've confirmed it's environmental, not a fluke.
- Bring one variable local at a time. Import the real data shape, then flip the flag to match, then match the user role — one change per attempt. The change that makes the bug appear is your root cause.
- Write the reproduction down as a test. The moment it reproduces, capture the trigger as a failing test so the fix is verifiable and the bug can't silently return.
When you still can't reproduce it locally
Sometimes the production environment simply can't be recreated on a laptop — the concurrency, the data volume, or the integration is the point. When that happens, stop trying to shrink production down and instead debug up in a production-like place:
- Add targeted logging around the failing path and let the next real occurrence tell you the input values you're missing. One well-placed log line often beats an hour of local guessing.
- Debug in staging with a restored copy of the affected data and the same flag configuration, so the environment matches without exposing live customer data.
- Instrument the request itself so the next failure automatically records the state you need — the inputs, the account, the environment — instead of asking the user to reproduce it on demand.
This is the same principle behind reducing "cannot reproduce" tickets: the fix is never a better guess, it's capturing more of the real state at the moment things break.
Prevent the next one
Production-only bugs are cheaper to stop than to chase. Seed your local and test environments with production-shaped data — including the ugly edge cases — rather than clean fixtures. Keep feature-flag and config differences between environments small and documented. And capture rich context on every bug report by default, so "only in production" becomes a solved input problem instead of a mystery. Klavity Snap attaches the URL, console, network, and environment to each report at the click, which turns most production-only reports into a repro you can act on the same day. For a wider view of the debugging tactics behind this, the Klavity blog covers evidence capture, triage, and turning fixes into regression tests.
Key takeaways
- Capture the failing production request, URL, and account state before you try to reproduce anything.
- Check the five parity gaps: data shape, config and flags, scale and concurrency, third-party integrations, and user or permission state.
- Change one production variable at a time when moving a repro from prod toward local, so you learn which one triggers the bug.
- If it won't reproduce locally, stop guessing and debug against a production-like environment instead.
FAQ
Why does a bug happen in production but not locally?
Because your local environment differs from production in at least one way the code depends on: the data shape, an environment variable or feature flag, request volume and concurrency, a third-party integration, or the specific user's account and permission state. The bug lives in that difference, not in the code path you can see locally.
How do I reproduce a bug I can only see in production?
Capture the failing request, URL, environment, and account state from production first — ideally at the exact moment it broke. Then reproduce it against a production-like environment, or bring one production variable at a time into your local setup (real data shape, the same flag values, the same user role) until the bug appears.
What's the difference between a production-only bug and a flaky bug?
A production-only bug is deterministic — it reproduces reliably given the right environment and inputs, it just never gets those inputs locally. A flaky bug is non-deterministic: it fails intermittently in the same environment due to timing, order, or races. Production-only bugs are an environment problem; flaky bugs are a timing problem.
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