Skip to content

The practical guide

How to reproduce a frontend bug.

Give the next person enough evidence to understand the starting state, follow the failure path, and inspect the final result without rerunning everything.

Renewal quote mismatchbilling.example / renewal
EXAMPLE
  1. Open seat renewal1 seat
    00:00
  2. Click “Add seat”2 seats
    00:04
  3. Click “Add seat” again3 seats
    00:08
  4. Click “Calculate renewal”Quote uses 2
    00:12
THE MISMATCH

Three seats on screen.
Two in the quote.

The second click is part of the story.

THE SHORT VERSION

Hand over the whole run, not just the last screen. Write down what was true before the first action, follow the path to the first mismatch, and show where things ended. Twelve parts cover it.

The packet gives someone else a place to start. It doesn’t prove the cause, confirm a fix, or promise the bug will happen again.

The 12-part evidence packet.

A screenshot shows where a bug ended. The next person also needs where it began and how it got there. The packet groups that evidence into three parts, one for each question they’ll ask.

  1. Starting state

    Understand where it began.

    1. Environment and build
    2. Test data, account, and role
    3. Flags, locale, and configuration
    4. State before the first step
  2. Failure path

    Follow what happened.

    1. Ordered actions
    2. Expected and actual at each step
    3. Browser state at the first mismatch
    4. Console context
    5. Network context
  3. Final result

    Inspect where it ended.

    1. Replay or trace
    2. Final-state evidence
    3. Last known passing

Fill in every part or write “unknown.” An honest unknown tells the next person where to look. A guess sends them somewhere else.

Understand the starting state.

Someone else needs to begin where you began. Write down what was true before the first action, while it’s still true.

  1. Environment and build

    The environment and URL, the build or commit, when it was deployed, the browser and its version, the operating system, and the viewport or zoom when layout matters.

    “Works for me” often means a different build. Copy the build from the app’s version endpoint, footer, or deploy log instead of writing it from memory.

  2. Test data, account, and role

    The account or records you used, the role and permissions behind them, the plan or entitlement, and any seed or fixture name. Use IDs. Never paste passwords or tokens.

    Name the data so someone else can find it or recreate it. “My test account” stops being useful when two accounts have different roles.

  3. Flags, locale, and configuration

    Feature flags and experiment variants, language and locale, time zone, currency, and region. Include browser settings that change behavior, such as blocked cookies.

    Record the value your session actually received. A flag’s default can differ from what your account saw.

  4. State before the first step

    The page you started on, whether you were signed in, how you got there, and anything already selected, open, filtered, or unsaved.

    Write this down before you refresh. A fresh tab can skip the earlier path that made the bug possible.

In the renewal example, “start with 1 seat” matters. Opening the page with 3 seats already saved would describe a different path.

Follow the failure path.

List what you did, what you expected after each action, and what happened. The first step where those differ is where the investigation starts.

  1. Ordered actions

    One action per step, in order. Use the label on the screen, the value you entered, and a time or replay timestamp. Keep repeated clicks, waits, and back navigation.

    “Click Add seat again” may be the step that matters. Don’t fold a repeated action into the one before it.

  2. Expected and actual at each step

    After each action, what should be true and what was true. Mark the first step where they differ, and say where the expectation comes from: a spec, a ticket, or a previous build.

    Compare specific values. “The quote is wrong” leaves the comparison to the reader.

  3. Browser state at the first mismatch

    What the page showed at that moment: the URL, the visible values, the affected element and its label, focus, loading indicators, and open dialogs. Add a screenshot with its timestamp.

    The first mismatch usually says more than the final error screen. A selector helps someone find the element. It doesn’t explain how the page got there.

  4. Console context

    Errors and warnings near the mismatch, with the step they follow. Note which log levels were visible and whether Preserve log was on. If the console was clean, say so.

    “No errors from 00:00 to 00:14” is evidence too. It tells the next person where not to start.

  5. Network context

    The requests tied to the failing steps: method, path, status, duration, and order. Add the one field that matters from the payload or response, when it’s safe to share.

    A 200 response can still carry the wrong value. Remove tokens, cookies, and personal data before you paste anything.

Where it’s safe to repeat the flow, run it again and remove one step at a time. Keep the shortest path that still fails, and note the steps you removed.

Collect console and network evidence in Chrome

Open DevTools before step 1. The Console clears when a new page loads, so turn on Preserve log in Console settings and in the Network panel.

Select a request to read its Payload, Response, and Timing. Copy only the field that matters into part 09.

To share the full network log, export the sanitized HAR. It leaves out Cookie, Set-Cookie, and Authorization headers, but URLs and bodies can still hold personal data. Review it before sharing.

To keep console output, use Save as… from the Console’s context menu. It saves the current messages to a .log file.

Chrome DevTools network reference

Inspect the final result.

Show where things ended, on screen and in saved data, and when the same path last worked. This is what lets someone check your conclusion without running the flow again first.

  1. Replay or trace

    A link to the recording or trace, the timestamp of the first mismatch, and what it covers: when it started, which tab, and what it leaves out.

    Start recording before step 1. A replay that begins after the important click shows the result without the path.

  2. Final-state evidence

    The final screen, plus what was saved, sent, or charged. Read it back from the API, an admin view, or the database instead of trusting the screen. Note where you stopped.

    Say what you didn’t do. “Stopped before Confirm renewal; no payment taken” saves someone a refund check.

  3. Last known passing

    The last build, date, or run where the same path worked, and what changed since: deploys, flags, data, or browser. If you don’t know, write unknown.

    Two known points turn a search into a comparison. With a good commit and a bad one, git bisect can narrow the range.

Choose a replay or trace that fits the failure.

Each kind of record answers a different question. Pick the one that shows the path, and say what it leaves out.

  • Screen video

    Shows

    What the reporter saw, including visual glitches and timing.

    Leaves out

    You can’t inspect elements, requests, or console messages inside it.

  • Session recording

    Shows

    The recorded page, step by step. Pause at a moment and inspect the elements on it.

    Leaves out

    It replays the recorded page, not the live app or its server data. Pair it with console and network notes.

  • Playwright trace

    Shows

    An automated run’s actions, DOM snapshots, console, network, and source in one file.

    Leaves out

    It exists only when tracing is on, and only for the automated run.

  • HAR file

    Shows

    Every request the Network panel kept, with timing, in a file others can import.

    Leaves out

    No page or console state. It can hold cookies, tokens, and personal data until you sanitize and review it.

For automated runs, keep traces for failures. Playwright’s retain-on-failure records each test and removes the trace when it passes.

playwright.config.ts
import { defineConfig } from "@playwright/test";

export default defineConfig({
  use: {
    // Keep traces from failed tests only.
    trace: "retain-on-failure",
  },
});

// Open a saved trace locally:
// npx playwright show-trace path/to/trace.zip

A complete browser-failure example.

Here’s the renewal bug as a full packet. Every part is filled in, including the ones that found nothing.

WORKED EXAMPLEIllustrative: fictional app, builds, and requests

Renewal quote uses 2 seats after a third seat is added.

Observed
Sep 24, 2026 · 15:12 EDT
Attempts
3 of 3 on build 2026.09.24.3
First mismatch
Step 4 at 00:12

Starting state

Parts 01 to 04
Environment and build
Staging · billing.example/renewal Build 2026.09.24.3 (commit 4f1c2ab), deployed Sep 24, 14:05 UTC Chrome 152 on macOS 26 1440 × 900 at 100% zoom
Test data, account, and role
Test team team_8812, seeded with renewal-fixture-1seat Billing admin: can edit seats and calculate renewals Annual plan, 1 seat at the start
Flags, locale, and configuration
seat-stepper-v2: on (enabled Sep 22) en-US, America/New_York USD, US billing region
State before the first step
Seat renewal for team_8812, showing 1 seat at $29/year Signed in; opened from Billing, then Renewal No dialogs open, no unsaved changes

Failure path

Parts 05 to 09
Actions, expected, and actual
  1. 1Open seat renewal00:00
    Expected
    1 seat · $29/year
    Actual
    1 seat · $29/year
  2. 2Click Add seat00:04
    Expected
    2 seats · $47/year
    Actual
    2 seats · $47/year
  3. 3Click Add seat again00:08
    Expected
    3 seats · $65/year
    Actual
    3 seats · $65/year
  4. 4Click Calculate renewal00:12
    Expected
    Quote: 3 seats · $65/year
    Actual
    Quote: 2 seats · $47/year

    First mismatch

Browser state at 00:12
URL
billing.example/renewal?team=team_8812
Seat count
3#visible-seats
Renewal total
$65/year#visible-total
Quote
2 seats · $47/year#request-state
Focus and loading
Calculate renewal focused; no spinner or dialog
Console

No errors or warnings from 00:00 to 00:14.

All log levels shown. Preserve log on.

Network
  1. 00:04PATCH /api/renewal/draft200 · 138 msseats: 2
  2. 00:08PATCH /api/renewal/draft200 · 141 msseats: 3
  3. 00:12POST /api/renewal/quote200 · 96 msseats: 2 → total: 47

The draft saved 3 seats. The quote request still sent 2.

Final result

Parts 10 to 12
Replay

Renewal quote mismatch

00:00 to 00:14, one tab, started before step 1. Server logs not covered.

Open the recording example
Final state

Page: 3 seats, $65/year. Quote: 2 seats, $47/year.

Read back: GET /api/renewal/draft returns seats: 3.

Stopped before Confirm renewal. No invoice or payment created.

Last known passing
  1. Sep 18Passed on build 2026.09.17.1
  2. Sep 22seat-stepper-v2 enabled for the team
  3. Sep 24Build 2026.09.24.3 deployed. Fails 3 of 3 attempts.

Not yet compared: the new build with the flag off.

WHAT IT SUPPORTS

Start with the code that builds the quote request. It sent 2 seats while the page and the saved draft had 3.

WHAT IT DOESN’T PROVE

Why the count was stale, whether the new build or the flag is responsible, or that a fix works.

The stale quote request is an observation. “The new build drops the second seat update” is a hypothesis to test, not a confirmed cause.

Copy or download the template.

Start with the blank packet, or open the filled example to see how the parts fit together. Copy it into your issue or download it as Markdown. Both include the handoff checklist.

Report version
Download template
YOUR NEXT EVIDENCE PACKETReplace the placeholders · Markdown, with the handoff checklist
Summary
[Action or condition] causes [specific unexpected result].
Observed
[Date, time, and time zone], by [name or role]
Attempts
[Occurrences] of [attempts]. [What changed between attempts.]

Starting state

Environment and build
Environment and URL: Build or commit, and deploy time: Browser and version, OS: Viewport, zoom, or device emulation:
Test data, account, and role
Account, team, or record IDs: Role and permissions: Plan, entitlement, or seed data:
Flags, locale, and configuration
Feature flags and variants: Locale, language, and time zone: Currency, region, or other configuration:
State before the first step
Starting page: Session, and how you got there: Already open, selected, filtered, or unsaved:

Failure path

Ordered actions
1. [Action, visible label, and value] at [time] 2. [Next action] at [time]
Expected and actual at each step
Steps that matched: Step [n] expected: [value] (source: [spec, ticket, or previous build]) Step [n] actual: [value] First mismatch: step [n] at [time]
Browser state at the first mismatch
URL: Visible values and affected element: Focus, loading state, open dialogs: Screenshot or replay timestamp:
Console context
Errors and warnings, with time and step: Log levels shown; Preserve log on or off:
Network context
[Time] [method] [path] · [status] · [duration] · [relevant field] Pending, failed, or retried requests:

Final result

Replay or trace
Recording or trace link: Covers: [start, end, tab] First mismatch at: Not covered:
Final-state evidence
Final screen: Saved or sent state, and how you checked it: Where you stopped:
Last known passing
Last passing build, date, or run: Changed since then: Not yet compared:

Know what the evidence can’t prove.

A good packet narrows the search. It doesn’t finish it. Keep these limits in the report so nobody reads more into it than it holds.

Can showWhat happened in the observed run, in order.
Does not proveThat the same thing will happen on the next run.
Can showThe first step where expected and actual differ.
Does not proveWhy they differ. The root cause is still a hypothesis.
Can showThe requests, errors, and values present near the mismatch.
Does not proveThat a nearby request or error caused it.
Can showHow the final state compares with the last passing run.
Does not proveThat a change, a rerun, or a replay has fixed it.

Label explanations as hypotheses. After a fix, repeat the documented path on the fixed build and compare the result with the expectation. Watching the earlier recording again doesn’t verify the fix.

When the bug won’t happen again.

Keep the original observation. A failed attempt to repeat the bug is useful information, but it does not establish that the issue has disappeared.

It works after a refresh.

Write down the path before the refresh: earlier pages, filters, selections, and repeated actions. Try that sequence again in a test environment before reducing it to a fresh-page example.

It happens only sometimes.

Record occurrences and attempts separately. Compare a passing and a failing attempt, including the order of actions, loading state, and the requests in part 09. Keep unknowns explicit.

Your teammate can’t reproduce it.

Compare the starting state first: build, account, role, flags, locale, and data in parts 01 to 04. Then check browser and viewport differences. Change one condition at a time so the comparison stays useful.

Investigating an automated failure? Start with the test run’s evidence in the flaky Playwright tests workflow.

Read it once as the person receiving it.

Before handing over the issue, check that your teammate has enough to begin.

  • Every part is filled in or marked unknown. Nothing is blank by accident.
  • The steps start from the written starting state and include repeated actions.
  • Expected and actual results name specific values, and the first mismatch is marked.
  • Observations are kept separate from guesses about the cause.
  • Attempts are reported honestly. An unknown frequency stays unknown.
  • Screenshots, logs, requests, and replays were checked for tokens and personal data.
  • The receiving teammate can open the evidence and use the same test data.

This packet supports investigation. It does not prove the root cause, confirm a fix, or guarantee the bug will happen again.

KEEP THE BROWSER PATH

Keep the failure path with the packet.

Start a recording before step 1. Replay the path, pause at the first mismatch, and inspect the affected element with the Samelogic extension. Link the recording in part 10.

Check the replay and written steps before sharing. Input values are masked by default, but other page content can still appear.

Free extension for desktop Chrome. Try it before signing up.

A few common questions.

What does it mean to reproduce a bug?

It means reaching the same unexpected behavior by following a described setup and sequence of actions. A recording documents an observed run; another attempt checks whether the behavior happens again.

How much evidence is enough?

Enough for someone else to start from your starting state, follow the path to the first mismatch, and check the final result without asking you what happened first. Fill in each of the 12 parts or mark it unknown. A short packet with honest unknowns is more useful than a long one that guesses.

Does this evidence prove the root cause?

No. It shows what happened in the observed run and where expected and actual first differ. A cause stays a hypothesis until someone tests it, and a fix is verified only by repeating the documented path on the fixed build.

What if I can’t reproduce it a second time?

Report the observation and say that another attempt did not reproduce it. Include the time, page, environment, earlier actions, and any evidence you kept. Record attempts separately from occurrences instead of calling the issue fixed or inventing a success rate.

Do I need a recording, a screenshot, or both?

A screenshot can show a layout issue or a final error. A recording makes the preceding actions and transitions easier to follow. Choose the evidence that explains the issue, and include the written starting state and steps either way.

Does replaying a recording reproduce the live bug?

Replaying the capture helps you inspect what happened in that recorded run. It does not establish that the live application will produce the same result now or restore its server data. Verify the behavior in an appropriate test environment.

Can Samelogic show what happened before I started recording?

No. You deliberately start and stop a Samelogic recording. Start before the actions that lead to the issue, then review the replay and written steps. Earlier actions need to be described or captured in a new attempt.

Sources & further reading

The packet follows established bug-reporting guidance and browser tool documentation. The renewal scenario is an authored example on a fictional app. Its builds, versions, requests, and attempts show the format; they are not customer data or a measured product result.

  1. Mozilla: bug-writing guidelines
  2. Chrome DevTools: console features reference
  3. Chrome DevTools: network features reference
  4. Playwright: trace viewer
  5. Git: git bisect